From 510ebf5b380ea167a92ab3426f257870f98b015f Mon Sep 17 00:00:00 2001 From: AJR Date: Wed, 18 Dec 2024 22:37:10 -0500 Subject: [PATCH] v25: Improve peripheral emulation - Add preliminary DMA controller (capable of doing burst memory transfers) - Make timer 1 cause two different interrupts - Add kludge for one timer edge case * tvdear: Map some more ROM; add P0 readback --- src/devices/cpu/nec/v25.cpp | 133 ++++++++++++++++++++++++++++++++- src/devices/cpu/nec/v25.h | 36 ++++++++- src/devices/cpu/nec/v25sfr.cpp | 133 +++++++++++++++++++++++++++++++-- src/mame/skeleton/tvdear.cpp | 12 ++- 4 files changed, 301 insertions(+), 13 deletions(-) diff --git a/src/devices/cpu/nec/v25.cpp b/src/devices/cpu/nec/v25.cpp index fac9865dff7..40dc190fa31 100644 --- a/src/devices/cpu/nec/v25.cpp +++ b/src/devices/cpu/nec/v25.cpp @@ -69,6 +69,8 @@ v25_common_device::v25_common_device(const machine_config &mconfig, device_type , m_p0_out(*this) , m_p1_out(*this) , m_p2_out(*this) + , m_dma_read(*this, 0xffff) + , m_dma_write(*this) , m_prefetch_size(prefetch_size) , m_prefetch_cycles(prefetch_cycles) , m_chip_type(chip_type) @@ -215,7 +217,6 @@ void v25_common_device::device_reset() m_intm = 0; m_halted = 0; - m_TM0 = m_MD0 = m_TM1 = m_MD1 = 0; m_TMC0 = m_TMC1 = 0; for (int i = 0; i < 2; i++) @@ -226,6 +227,10 @@ void v25_common_device::device_reset() m_sce[i] = 0; } + m_dmam[0] = m_dmam[1] = 0; + m_dma_channel = -1; + m_last_dma_channel = 0; + m_RAMEN = 1; m_TB = 20; m_PCK = 8; @@ -483,6 +488,111 @@ void v25_common_device::external_int() } } +void v25_common_device::dma_process() +{ + uint16_t sar = m_internal_ram[m_dma_channel * 4]; + uint16_t dar = m_internal_ram[m_dma_channel * 4 + 1]; + uint16_t sarh_darh = m_internal_ram[m_dma_channel * 4 + 2]; + uint8_t dmamode = BIT(m_dmam[m_dma_channel], 5, 3); + bool w = BIT(m_dmam[m_dma_channel], 4); + + uint32_t saddr = (BIT(dmamode, 0) ? 0 : (uint32_t(sarh_darh) & 0xff00) << 4) + sar; + uint32_t daddr = (BIT(dmamode, 1) ? 0 : (uint32_t(sarh_darh) & 0x00ff) << 12) + dar; + + switch (dmamode & 3) + { + case 0: + // Memory to memory transfer + if (w) + { + uint16_t data = v25_read_word(saddr); + v25_write_word(daddr, data); + } + else + { + uint8_t data = v25_read_byte(saddr); + v25_write_byte(daddr, data); + } + m_icount -= (w && m_program->addr_width() == 8) ? 8 : 4; + break; + + case 1: + // I/O to memory transfer + if (w && m_program->addr_width() == 16) + { + uint16_t data = m_dma_read[m_dma_channel](daddr); + v25_write_word(daddr, data); + } + else + { + uint8_t data = m_dma_read[m_dma_channel](daddr); + v25_write_byte(daddr, data); + if (w) + { + logerror("Warning: V25 16-bit I/O to memory transfer\n"); + data = m_dma_read[m_dma_channel](daddr + 1); + v25_write_byte(daddr + 1, data); + m_icount -= 2; + } + } + m_icount -= 2; + break; + + case 2: + // Memory to I/O transfer + if (w && m_program->addr_width() == 16) + { + uint16_t data = v25_read_word(saddr); + m_dma_write[m_dma_channel](saddr, data); + } + else + { + uint8_t data = v25_read_byte(saddr); + m_dma_write[m_dma_channel](saddr, data); + if (w) + { + logerror("Warning: V25 16-bit memory to I/O transfer\n"); + data = v25_read_byte(saddr + 1); + m_dma_write[m_dma_channel](saddr + 1, data); + m_icount -= 2; + } + } + m_icount -= 2; + break; + + default: + logerror("Reserved DMA transfer mode\n"); + m_icount--; + break; + } + + // Update source and destination based on address control + uint8_t dmac = m_dmac[m_dma_channel]; + if (BIT(dmac, 0, 2) == 1) + m_internal_ram[m_dma_channel * 4] = sar + (w ? 2 : 1); + else if (BIT(dmac, 0, 2) == 2) + m_internal_ram[m_dma_channel * 4] = sar - (w ? 2 : 1); + if (BIT(dmac, 4, 2) == 1) + m_internal_ram[m_dma_channel * 4 + 1] = dar + (w ? 2 : 1); + else if (BIT(dmac, 4, 2) == 2) + m_internal_ram[m_dma_channel * 4 + 1] = dar - (w ? 2 : 1); + + // Update TC + uint16_t tc = --m_internal_ram[m_dma_channel * 4 + 3]; + if (tc == 0) + { + m_dmam[m_dma_channel] &= 0xf0; // disable channel + m_pending_irq |= m_dma_channel ? INTD1 : INTD0; // request interrupt + } + + if (dmamode == 0 || dmamode > 4 || tc == 0) + { + // Single step/single transfer modes (or end of burst) + m_last_dma_channel = m_dma_channel; + m_dma_channel = -1; + } +} + /****************************************************************************/ /* OPCODES */ /****************************************************************************/ @@ -570,6 +680,9 @@ void v25_common_device::device_start() m_EO = 0; m_E16 = 0; + m_TM0 = m_MD0 = m_TM1 = m_MD1 = 0; + m_dmac[0] = m_dmac[1] = 0; + for (i = 0; i < 4; i++) m_timers[i] = timer_alloc(FUNC(v25_common_device::v25_timer_callback), this); @@ -630,6 +743,10 @@ void v25_common_device::device_start() save_item(NAME(m_scc)); save_item(NAME(m_brg)); save_item(NAME(m_sce)); + save_item(NAME(m_dmac)); + save_item(NAME(m_dmam)); + save_item(NAME(m_dma_channel)); + save_item(NAME(m_last_dma_channel)); save_item(NAME(m_RAMEN)); save_item(NAME(m_TB)); save_item(NAME(m_PCK)); @@ -790,6 +907,12 @@ void v25_common_device::execute_run() } while(m_icount>0) { + if (m_dma_channel != -1) + { + dma_process(); + continue; + } + /* Dispatch IRQ */ m_prev_ip = m_ip; if (m_no_interrupt==0 && (m_pending_irq & m_unmasked_irq)) @@ -808,5 +931,13 @@ void v25_common_device::execute_run() prev_ICount = m_icount; (this->*s_nec_instruction[fetchop()])(); do_prefetch(prev_ICount); + + if ((m_dmam[0] & 0x0c) == 0x0c || (m_dmam[1] & 0x0c) == 0x0c) + { + if ((m_dmam[1 - m_last_dma_channel] & 0x0c) == 0x0c) + m_dma_channel = 1 - m_last_dma_channel; + else + m_dma_channel = m_last_dma_channel; + } } } diff --git a/src/devices/cpu/nec/v25.h b/src/devices/cpu/nec/v25.h index 2c030f5e728..73b09844a19 100644 --- a/src/devices/cpu/nec/v25.h +++ b/src/devices/cpu/nec/v25.h @@ -39,6 +39,12 @@ public: auto p1_out_cb() { return m_p1_out.bind(); } auto p2_out_cb() { return m_p2_out.bind(); } + auto dma0_read_cb() { return m_dma_read[0].bind(); } + auto dma1_read_cb() { return m_dma_read[1].bind(); } + + auto dma0_write_cb() { return m_dma_write[0].bind(); } + auto dma1_write_cb() { return m_dma_write[1].bind(); } + TIMER_CALLBACK_MEMBER(v25_timer_callback); protected: @@ -113,19 +119,25 @@ private: uint8_t m_no_interrupt; uint8_t m_halted; - /* timer related */ + // timer related uint16_t m_TM0, m_MD0, m_TM1, m_MD1; uint8_t m_TMC0, m_TMC1; emu_timer *m_timers[4]; - /* serial interface related */ + // serial interface related uint8_t m_scm[2]; uint8_t m_scc[2]; uint8_t m_brg[2]; uint8_t m_sce[2]; - /* system control */ - uint8_t m_RAMEN, m_TB, m_PCK; /* PRC register */ + // DMA related + uint8_t m_dmac[2]; + uint8_t m_dmam[2]; + int8_t m_dma_channel; + int8_t m_last_dma_channel; + + // system control + uint8_t m_RAMEN, m_TB, m_PCK; // PRC register uint8_t m_RFM; uint16_t m_WTC; uint32_t m_IDB; @@ -146,6 +158,9 @@ private: devcb_write8 m_p1_out; devcb_write8 m_p2_out; + devcb_read16::array<2> m_dma_read; + devcb_write16::array<2> m_dma_write; + uint8_t m_prefetch_size; uint8_t m_prefetch_cycles; int8_t m_prefetch_count; @@ -177,6 +192,7 @@ private: void nec_bankswitch(unsigned bank_num); void nec_trap(); void external_int(); + void dma_process(); void ida_sfr_map(address_map &map) ATTR_COLD; uint8_t read_irqcontrol(int /*INTSOURCES*/ source, uint8_t priority); @@ -257,6 +273,18 @@ private: void tmic1_w(uint8_t d); uint8_t tmic2_r(); void tmic2_w(uint8_t d); + uint8_t dmac0_r(); + void dmac0_w(uint8_t d); + uint8_t dmam0_r(); + void dmam0_w(uint8_t d); + uint8_t dmac1_r(); + void dmac1_w(uint8_t d); + uint8_t dmam1_r(); + void dmam1_w(uint8_t d); + uint8_t dic0_r(); + void dic0_w(uint8_t d); + uint8_t dic1_r(); + void dic1_w(uint8_t d); uint8_t rfm_r(); void rfm_w(uint8_t d); uint16_t wtc_r(); diff --git a/src/devices/cpu/nec/v25sfr.cpp b/src/devices/cpu/nec/v25sfr.cpp index a41bfb8c8f6..44d01e77999 100644 --- a/src/devices/cpu/nec/v25sfr.cpp +++ b/src/devices/cpu/nec/v25sfr.cpp @@ -57,6 +57,12 @@ void v25_common_device::ida_sfr_map(address_map &map) map(0x19c, 0x19c).rw(FUNC(v25_common_device::tmic0_r), FUNC(v25_common_device::tmic0_w)); map(0x19d, 0x19d).rw(FUNC(v25_common_device::tmic1_r), FUNC(v25_common_device::tmic1_w)); map(0x19e, 0x19e).rw(FUNC(v25_common_device::tmic2_r), FUNC(v25_common_device::tmic2_w)); + map(0x1a0, 0x1a0).rw(FUNC(v25_common_device::dmac0_r), FUNC(v25_common_device::dmac0_w)); + map(0x1a1, 0x1a1).rw(FUNC(v25_common_device::dmam0_r), FUNC(v25_common_device::dmam0_w)); + map(0x1a2, 0x1a2).rw(FUNC(v25_common_device::dmac1_r), FUNC(v25_common_device::dmac1_w)); + map(0x1a3, 0x1a3).rw(FUNC(v25_common_device::dmam1_r), FUNC(v25_common_device::dmam1_w)); + map(0x1ac, 0x1ac).rw(FUNC(v25_common_device::dic0_r), FUNC(v25_common_device::dic0_w)); + map(0x1ad, 0x1ad).rw(FUNC(v25_common_device::dic1_r), FUNC(v25_common_device::dic1_w)); map(0x1e1, 0x1e1).rw(FUNC(v25_common_device::rfm_r), FUNC(v25_common_device::rfm_w)); map(0x1e8, 0x1e9).rw(FUNC(v25_common_device::wtc_r), FUNC(v25_common_device::wtc_w)); map(0x1ea, 0x1ea).rw(FUNC(v25_common_device::flag_r), FUNC(v25_common_device::flag_w)); @@ -469,7 +475,13 @@ uint16_t v25_common_device::md1_r() void v25_common_device::md1_w(uint16_t d) { - m_MD1 = d; + if (m_MD1 == 0 && d != 0) + { + m_MD1 = d; + tmc1_w(m_TMC1); // HACK: start timer if necessary + } + else + m_MD1 = d; } void v25_common_device::tmc0_w(uint8_t d) @@ -477,7 +489,7 @@ void v25_common_device::tmc0_w(uint8_t d) m_TMC0 = d; if (BIT(d, 0)) // oneshot mode { - if (BIT(d, 7)) + if (BIT(d, 7) && m_TM0 != 0) { unsigned tmp = m_PCK * m_TM0 * (BIT(d, 6) ? 128 : 12); attotime time = clocks_to_attotime(tmp); @@ -486,7 +498,7 @@ void v25_common_device::tmc0_w(uint8_t d) else m_timers[0]->adjust(attotime::never); - if (BIT(d, 5)) + if (BIT(d, 5) && m_MD0 != 0) { unsigned tmp = m_PCK * m_MD0 * (BIT(d, 4) ? 128 : 12); attotime time = clocks_to_attotime(tmp); @@ -497,7 +509,7 @@ void v25_common_device::tmc0_w(uint8_t d) } else // interval mode { - if (BIT(d, 7)) + if (BIT(d, 7) && m_MD0 != 0) { unsigned tmp = m_PCK * m_MD0 * (BIT(d, 6) ? 128 : 6); attotime time = clocks_to_attotime(tmp); @@ -516,11 +528,11 @@ void v25_common_device::tmc0_w(uint8_t d) void v25_common_device::tmc1_w(uint8_t d) { m_TMC1 = d & 0xC0; - if (BIT(d, 7)) + if (BIT(d, 7) && m_MD1 != 0) { unsigned tmp = m_PCK * m_MD1 * (BIT(d, 6) ? 128 : 6); attotime time = clocks_to_attotime(tmp); - m_timers[2]->adjust(time, INTTU2, time); + m_timers[2]->adjust(time, INTTU1 | INTTU2, time); m_TM1 = m_MD1; } else @@ -569,6 +581,115 @@ void v25_common_device::tmic2_w(uint8_t d) write_irqcontrol(INTTU2, d); } +uint8_t v25_common_device::dmac0_r() +{ + return m_dmac[0]; +} + +void v25_common_device::dmac0_w(uint8_t d) +{ + logerror("%06x: DMAC0 set to %02x\n", PC(), d); + logerror(" SAR0 %s after each transfer\n", (d & 0x03) == 0x01 ? "incremented" : (d & 0x03) == 0x02 ? "decremented" : "unmodified"); + logerror(" DAR0 %s after each transfer\n", (d & 0x30) == 0x10 ? "incremented" : (d & 0x30) == 0x20 ? "decremented" : "unmodified"); + m_dmac[0] = d & 0x33; +} + +uint8_t v25_common_device::dmam0_r() +{ + return m_dmam[0]; +} + +void v25_common_device::dmam0_w(uint8_t d) +{ + logerror("%06x: DMAM0 set to %02x\n", PC(), d); + if ((d & 0x60) == 0) + logerror(" %s mode, memory to memory, %s\n", BIT(d, 7) ? "Single step" : "Burst", BIT(d, 4) ? "words" : "bytes"); + else if ((d & 0x60) != 0x60) + logerror(" %s mode, %s to %s, %s\n", BIT(d, 7) ? "Single transfer" : "Demand release", + BIT(d, 5) ? "I/O" : "memory", + BIT(d, 6) ? "I/O" : "memory", + BIT(d, 4) ? "words" : "bytes"); + if (BIT(d, 2)) + { + uint16_t sar = m_internal_ram[0]; + uint16_t dar = m_internal_ram[1]; + uint16_t sarh_darh = m_internal_ram[2]; + uint16_t tc = m_internal_ram[3]; + logerror(" DMA enabled%s (%04x:%04x -> %04x:%04x, %u %s)\n", BIT(d, 3) ? " and triggered" : "", + sarh_darh & 0xff00, sar, + (sarh_darh & 0x00ff) << 8, dar, + tc, BIT(d, 4) ? "words" : "bytes"); + } + else + logerror(" DMA not enabled\n"); + m_dmam[0] = d & 0xfc; +} + +uint8_t v25_common_device::dmac1_r() +{ + return m_dmac[1]; +} + +void v25_common_device::dmac1_w(uint8_t d) +{ + logerror("%06x: DMAC1 set to %02x\n", PC(), d); + logerror(" SAR1 %s after each transfer\n", (d & 0x03) == 0x01 ? "incremented" : (d & 0x03) == 0x02 ? "decremented" : "unmodified"); + logerror(" DAR1 %s after each transfer\n", (d & 0x30) == 0x10 ? "incremented" : (d & 0x30) == 0x20 ? "decremented" : "unmodified"); + m_dmac[1] = d & 0x33; +} + +uint8_t v25_common_device::dmam1_r() +{ + return m_dmam[1]; +} + +void v25_common_device::dmam1_w(uint8_t d) +{ + logerror("%06x: DMAM1 set to %02x\n", PC(), d); + if ((d & 0x60) == 0) + logerror(" %s mode, memory to memory, %s\n", BIT(d, 7) ? "Single step" : "Burst", BIT(d, 4) ? "words" : "bytes"); + else if ((d & 0x60) != 0x60) + logerror(" %s mode, %s to %s, %s\n", BIT(d, 7) ? "Single transfer" : "Demand release", + BIT(d, 5) ? "I/O" : "memory", + BIT(d, 6) ? "I/O" : "memory", + BIT(d, 4) ? "words" : "bytes"); + if (BIT(d, 2)) + { + uint16_t sar = m_internal_ram[4]; + uint16_t dar = m_internal_ram[5]; + uint16_t sarh_darh = m_internal_ram[6]; + uint16_t tc = m_internal_ram[7]; + logerror(" DMA enabled%s (%04x:%04x -> %04x:%04x, %u %s)\n", BIT(d, 3) ? " and triggered" : "", + sarh_darh & 0xff00, sar, + (sarh_darh & 0x00ff) << 8, dar, + tc, BIT(d, 4) ? "words" : "bytes"); + } + else + logerror(" DMA not enabled\n"); + m_dmam[1] = d & 0xfc; +} + +uint8_t v25_common_device::dic0_r() +{ + return read_irqcontrol(INTD0, m_priority_intd); +} + +void v25_common_device::dic0_w(uint8_t d) +{ + write_irqcontrol(INTD0, d); + m_priority_intd = d & 0x7; +} + +uint8_t v25_common_device::dic1_r() +{ + return read_irqcontrol(INTD1, 7); +} + +void v25_common_device::dic1_w(uint8_t d) +{ + write_irqcontrol(INTD1, d); +} + uint8_t v25_common_device::rfm_r() { return m_RFM; diff --git a/src/mame/skeleton/tvdear.cpp b/src/mame/skeleton/tvdear.cpp index ddcb4b1bdcf..a3d63466a7f 100644 --- a/src/mame/skeleton/tvdear.cpp +++ b/src/mame/skeleton/tvdear.cpp @@ -25,6 +25,7 @@ protected: virtual void machine_start() override ATTR_COLD; private: + u8 p0_r(); void p0_w(u8 data); u8 pt_r(); @@ -42,6 +43,11 @@ void tvdear_state::machine_start() save_item(NAME(m_p0)); } +u8 tvdear_state::p0_r() +{ + return m_p0; +} + void tvdear_state::p0_w(u8 data) { m_p0 = data; @@ -59,7 +65,7 @@ void tvdear_state::mem_map(address_map &map) { map(0x00000, 0x07fff).ram(); map(0x10000, 0x17fff).ram(); - map(0x80000, 0xfffff).rom().region("maincpu", 0x080000); + map(0x40000, 0xfffff).rom().region("maincpu", 0x040000); } void tvdear_state::io_map(address_map &map) @@ -174,6 +180,7 @@ void tvdear_state::tvdear(machine_config &config) V25(config, m_maincpu, 16000000); // NEC D70320DGJ-8; XTAL marked 16AKSS5HT m_maincpu->set_addrmap(AS_PROGRAM, &tvdear_state::mem_map); m_maincpu->set_addrmap(AS_IO, &tvdear_state::io_map); + m_maincpu->p0_in_cb().set(FUNC(tvdear_state::p0_r)); m_maincpu->p0_out_cb().set(FUNC(tvdear_state::p0_w)); m_maincpu->pt_in_cb().set(FUNC(tvdear_state::pt_r)); @@ -189,4 +196,5 @@ ROM_END } // anonymous namespace -CONS( 1995, tvdear, 0, 0, tvdear, tvdear, tvdear_state, empty_init, "Takara", "TV Dear Multi Word Processor", MACHINE_IS_SKELETON | MACHINE_NODEVICE_PRINTER ) // テレビディア マルチワープロ +// テレビディア マルチワープロ +CONS( 1995, tvdear, 0, 0, tvdear, tvdear, tvdear_state, empty_init, "Takara", "TV Dear Multi Word Processor", MACHINE_IS_SKELETON | MACHINE_NODEVICE_PRINTER )