// license:BSD-3-Clause // copyright-holders:Olivier Galibert #include "emu.h" #include "maple-dc.h" #include "mie.h" DEFINE_DEVICE_TYPE(MAPLE_DC, maple_dc_device, "maple_dc", "Dreamcast Maple Bus") void maple_dc_device::amap(address_map &map) { map(0x04, 0x07).rw(FUNC(maple_dc_device::sb_mdstar_r), FUNC(maple_dc_device::sb_mdstar_w)); map(0x10, 0x13).rw(FUNC(maple_dc_device::sb_mdtsel_r), FUNC(maple_dc_device::sb_mdtsel_w)); map(0x14, 0x17).rw(FUNC(maple_dc_device::sb_mden_r), FUNC(maple_dc_device::sb_mden_w)); map(0x18, 0x1b).rw(FUNC(maple_dc_device::sb_mdst_r), FUNC(maple_dc_device::sb_mdst_w)); map(0x80, 0x83).rw(FUNC(maple_dc_device::sb_msys_r), FUNC(maple_dc_device::sb_msys_w)); map(0x8c, 0x8f).w(FUNC(maple_dc_device::sb_mdapro_w)); } maple_dc_device::maple_dc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MAPLE_DC, tag, owner, clock), cpu(*this, finder_base::DUMMY_TAG), irq_cb(*this) { // Do not move that in device_start or there will be a race // condition with the maple devices call to register_port. memset(devices, 0, sizeof(devices)); } void maple_dc_device::register_port(int port, maple_device *device) { if(devices[port]) fatalerror("maple_dc_device: duplicate registration on port %d\n", port); devices[port] = device; } void maple_dc_device::device_start() { logerror("maple_dc_device started\n"); timer = timer_alloc(0); irq_cb.resolve_safe(); mdstar = 0; save_item(NAME(mdstar)); save_item(NAME(mden)); save_item(NAME(mdst)); save_item(NAME(msys)); save_item(NAME(mdtsel)); save_item(NAME(dma_state)); save_item(NAME(dma_adr)); save_item(NAME(dma_port)); save_item(NAME(dma_dest)); save_item(NAME(dma_endflag)); } void maple_dc_device::device_reset() { mden = 0; mdst = 0; msys = 0; mdtsel = 0; dma_state = DMA_IDLE; dma_adr = 0; dma_port = 0; dma_dest = 0; dma_endflag = false; } void maple_dc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { timer.adjust(attotime::never); switch(dma_state) { case DMA_WAIT_REPLY: dma_state = DMA_TIMEOUT; dma_step(); break; case DMA_WAIT_NOP: dma_state = DMA_SEND; dma_step(); break; case DMA_DONE: dma_state = DMA_IDLE; mdst = 0; irq_cb(DMA_MAPLE_IRQ); break; default: logerror("MAPLE: Unexpected timer callback trigger\n"); break; } } void maple_dc_device::dma_step() { for(;;) { switch(dma_state) { case DMA_SEND: { sh4_ddt_dma ddtdata; uint32_t header[2]; uint32_t data[512]; ddtdata.source = dma_adr; // source address ddtdata.length = 2; // words to transfer ddtdata.size = 4; // bytes per word ddtdata.buffer = header; // destination buffer ddtdata.direction = 0; // 0 source to buffer, 1 buffer to source ddtdata.channel = 0; ddtdata.mode = -1; // copy from/to buffer cpu->sh4_dma_ddt(&ddtdata); dma_adr += 8; dma_endflag = header[0] & 0x80000000; dma_port = (header[0] >> 16) & 3; uint32_t pattern = (header[0] >> 8) & 7; uint32_t length = (header[0] & 255) + 1; dma_dest = header[1]; ddtdata.source = dma_adr; // source address ddtdata.length = length; // words to transfer ddtdata.size = 4; // bytes per word ddtdata.buffer = data; // destination buffer ddtdata.direction = 0; // 0 source to buffer, 1 buffer to source ddtdata.channel = 0; ddtdata.mode = -1; // copy from/to buffer cpu->sh4_dma_ddt(&ddtdata); dma_adr += length*4; switch(pattern) { case 0: // start if(devices[dma_port]) devices[dma_port]->maple_w(data, length); else { // Avoid spending time on that specific timeout dma_state = DMA_TIMEOUT; break; } dma_state = DMA_WAIT_REPLY; // the MIE seems too slow for the correct timeout // it's rather strange though // timer->adjust(attotime::from_nsec(40000 + 20*(msys>>16))); // The 40us represent the sending and reception time timer->adjust(attotime::from_msec(5)); break; case 2: // sdckb occupy permission (light gun protocol) logerror("MAPLE: sdckb occupy permission\n"); break; case 3: // reset if(devices[dma_port]) devices[dma_port]->maple_reset(); break; case 4: // sdckb occupy cancel logerror("MAPLE: sdckb occupy cancel\n"); break; case 7: // nop logerror("MAPLE: nop\n"); dma_state = DMA_WAIT_NOP; break; } if(dma_state == DMA_SEND && dma_endflag) dma_state = DMA_DONE; break; } case DMA_WAIT_REPLY: return; case DMA_WAIT_NOP: return; case DMA_TIMEOUT: { sh4_ddt_dma ddtdata; uint32_t data = 0xffffffff; ddtdata.destination = dma_dest; // destination address ddtdata.length = 1; // words to transfer ddtdata.size = 4; // bytes per word ddtdata.buffer = &data; // destination buffer ddtdata.direction = 1; // 0 source to buffer, 1 buffer to source ddtdata.channel = 0; ddtdata.mode = -1; // copy from/to buffer cpu->sh4_dma_ddt(&ddtdata); dma_state = dma_endflag ? DMA_DONE : DMA_SEND; break; } case DMA_GOT_REPLY: { timer->adjust(attotime::never); sh4_ddt_dma ddtdata; uint32_t data[512]; uint32_t length = 0; bool partial = false; if(devices[dma_port]) devices[dma_port]->maple_r(data, length, partial); else fatalerror("MAPLE: reading from unconnected device on port %d\n", dma_port); if(length) { ddtdata.destination = dma_dest; // destination address ddtdata.length = length; // words to transfer ddtdata.size = 4; // bytes per word ddtdata.buffer = data; // destination buffer ddtdata.direction = 1; // 0 source to buffer, 1 buffer to source ddtdata.channel = 0; ddtdata.mode = -1; // copy from/to buffer cpu->sh4_dma_ddt(&ddtdata); dma_dest += length*4; } if(partial) dma_state = DMA_WAIT_REPLY; else dma_state = dma_endflag ? DMA_DONE : DMA_SEND; break; } case DMA_DONE: timer->adjust(attotime::from_usec(200)); return; } } } void maple_dc_device::end_of_reply() { if(dma_state == DMA_WAIT_REPLY) { dma_state = DMA_GOT_REPLY; dma_step(); } else logerror("MAPLE: Unexpected end of reply\n"); } void maple_dc_device::maple_hw_trigger() { if(mdtsel & 1) // HW trigger { dma_adr = mdstar; dma_state = DMA_SEND; dma_step(); } } READ32_MEMBER(maple_dc_device::sb_mdstar_r) { return mdstar; } WRITE32_MEMBER(maple_dc_device::sb_mdstar_w) { mdstar = data & ~31; } READ32_MEMBER(maple_dc_device::sb_mden_r) { return mden; } WRITE32_MEMBER(maple_dc_device::sb_mden_w) { mden = data & 1; } READ32_MEMBER(maple_dc_device::sb_mdtsel_r) { return mdtsel; } WRITE32_MEMBER(maple_dc_device::sb_mdtsel_w) { mdtsel = data & 1; } READ32_MEMBER(maple_dc_device::sb_mdst_r) { return dma_state != DMA_IDLE ? 1 : 0; } WRITE32_MEMBER(maple_dc_device::sb_mdst_w) { uint32_t old = mdst; mdst = data & 1; if(!old && data && (mden & 1) && mdtsel == 0) { dma_adr = mdstar; dma_state = DMA_SEND; dma_step(); } } READ32_MEMBER(maple_dc_device::sb_msys_r) { return msys; } WRITE32_MEMBER(maple_dc_device::sb_msys_w) { msys = data; } WRITE32_MEMBER(maple_dc_device::sb_mdapro_w) { }