diff options
author | 2019-12-18 12:35:45 +0100 | |
---|---|---|
committer | 2019-12-18 12:35:45 +0100 | |
commit | 73732ffb3956a3896bffa51c91ba1c4759ecef04 (patch) | |
tree | cf1681b77a7313cdb68cbbac0871850d6ed0a47f /src | |
parent | 2e1ba819b62ed3a1158baded689920981cc1ffc4 (diff) |
WIP NEW DEVICE Motorola mc6844 DMA controller (devicified from swtpc09.cpp)
Diffstat (limited to 'src')
-rw-r--r-- | src/devices/machine/mc6844.cpp | 556 | ||||
-rw-r--r-- | src/devices/machine/mc6844.h | 148 |
2 files changed, 704 insertions, 0 deletions
diff --git a/src/devices/machine/mc6844.cpp b/src/devices/machine/mc6844.cpp new file mode 100644 index 00000000000..6e5ca560f7e --- /dev/null +++ b/src/devices/machine/mc6844.cpp @@ -0,0 +1,556 @@ +// license:BSD-3-Clause +// copyright-holders: Joakim Larsson Edström +/********************************************************************** + + Motorola 6844 emulation. This code is not yet ready for general use, see TODO list below + + "MC6844 — Direct Memory Access Controller + + This DMAC works with an M6800 MPU Clock Pulse Generator and an I/O Peripheral Controller, + such as the units described here, to facilitate direct access to the computer memory by + the peripheral, thus by passing MPU interactive time delay. + + General Description + + The MC6844 is operable in three modes: HALT Burst, Cycle Steal and TSC Steal. + In the Burst Mode, the MPU is halted by the first transfer request (TxRQ) input and + is restarted when the Byte Count Register (BCR) is zero. Each data transfer is synchronized + by a pulse input of TxRQ. In the Cycle Steal Mode, the MPU is halted by each TxRQ and + is restarted after each one byte of data transferred. In the TSC Steal Mode, DMAC uses the + three-state control function of the MPU to control the system bus. One byte of data is + transferred during each DMA cycle. + + The DMAC has four channels. A Priority Control Register determines which of the channels + is enabled. While data is being transferred on one channel, the other channels are inhibited. + When one channel completes transferring, the next will become valid for DMA transfer. The PCR + also utilizes a Rotate Control bit. Priority of DMA transfer is normally fixed in sequential + order. The highest priority is in #0 Channel and the lowest is in #3. When this bit is in high + level, channel priority is rotated such that the just-serviced channel has the lowest priority + in the next DMA transfer." + + Source: https://en.wikipedia.org/wiki/File:Motorola_Microcomputer_Components_1978_pg13.jpg + + CREDITS & Prior Work: + The base code was ripped out of swtpc09.cpp and deviceified but similar code is also to be found + in exidy440.cpp so copyrigt is probably shared among the authors there: Robert Justice, 68bit and + Aaron Giles. + + TODO: + - Memory to Device transfers + +**********************************************************************/ + +#include "emu.h" +#include "mc6844.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** +#define LOG_SETUP (1U << 1) +#define LOG_INT (1U << 2) +#define LOG_STATE (1U << 3) +#define LOG_TFR (1U << 4) + +//#define VERBOSE (LOG_GENERAL|LOG_SETUP | LOG_INT | LOG_STATE |LOG_TFR) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__) +#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__) +#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__) +#define LOGTFR(...) LOGMASKED(LOG_TFR, __VA_ARGS__) + +#ifdef _MSC_VER +#define FUNCNAME __func__ +#else +#define FUNCNAME __PRETTY_FUNCTION__ +#endif + +// device type definition +DEFINE_DEVICE_TYPE(MC6844, mc6844_device, "mc6844", "MC6844 DMA") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// mc6844_device - constructor +//------------------------------------------------- +mc6844_device::mc6844_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, MC6844, tag, owner, clock) + , device_execute_interface(mconfig, *this) + , m_out_int_cb(*this) + , m_out_txak_cb(*this) + , m_out_drq1_cb(*this) + , m_out_drq2_cb(*this) + , m_in_memr_cb(*this) + , m_out_memw_cb(*this) + , m_in_ior_cb{ { *this },{ *this },{ *this },{ *this } } + , m_out_iow_cb{ { *this },{ *this },{ *this },{ *this } } + , m_state(STATE_S0) + , m_icount(0) +{ +} + +//------------------------------------------------- +// device_resolve_objects - device-specific setup +//------------------------------------------------- +void mc6844_device::device_resolve_objects() +{ + m_out_int_cb.resolve_safe(); + m_out_txak_cb.resolve_safe(); + m_out_drq1_cb.resolve_safe(); + m_out_drq2_cb.resolve_safe(); + m_in_memr_cb.resolve_safe(0); + m_out_memw_cb.resolve_safe(); + + for(auto &cb : m_in_ior_cb) + cb.resolve_safe(0); + for(auto &cb : m_out_iow_cb) + cb.resolve_safe(); + +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc6844_device::device_start() +{ + // set our instruction counter + set_icountptr(m_icount); + + save_item(NAME(m_m6844_priority)); + save_item(NAME(m_m6844_interrupt)); + save_item(NAME(m_m6844_chain)); + save_item(NAME(m_state)); + save_item(NAME(m_icount)); + save_item(NAME(m_current_channel)); + save_item(NAME(m_last_channel)); + save_item(NAME(m_dgrnt)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc6844_device::device_reset() +{ + // reset the 6844 + for (int i = 0; i < 4; i++) + { + m_m6844_channel[i].active = 0; + m_m6844_channel[i].control = 0x00; + } + m_m6844_priority = 0x00; + m_m6844_interrupt = 0x00; + m_m6844_chain = 0x00; + m_state = STATE_SI; +} + +//------------------------------------------------- +// dma_request - +//------------------------------------------------- + +void mc6844_device::dma_request(int channel, int state) +{ + LOG("MC6844 Channel %u DMA Request: %u\n", channel, state); + + m_dreq[channel & 3] = state; + + LOGSTATE("Trigger(1)\n"); + trigger(1); +} + +//------------------------------------------------- +// execute_run - +//------------------------------------------------- + +void mc6844_device::execute_run() +{ + do + { + switch (m_state) + { + case STATE_SI: // IDLE state, will suspend until a DMA request comes through + { // Hi ------> Lo + int const priorities[][4] = {{ 1, 2, 3, 0 }, + { 2, 3, 0, 1 }, + { 3, 0, 1, 2 }, + { 0, 1, 2, 3 }}; + + LOGSTATE("DMA state SI\n"); + for (int prio = 0; prio < 4; prio++) + { + // Rotating or static channel prioritizations + int current_channel = priorities[((m_m6844_priority & 0x80) ? m_last_channel : 3) & 3][prio]; + + if (m_m6844_channel[current_channel].active == 1 && m_dreq[current_channel] == ASSERT_LINE) + { + m_current_channel = m_last_channel = current_channel; + m_state = STATE_S0; + break; + } + } + } + if(m_state == STATE_SI) + { + LOGSTATE("Suspend in SI\n"); + suspend_until_trigger(1, true); + m_icount = 0; + } + break; + case STATE_S0: // Wait for BCR != 0 and Tx EN == 1 + LOGSTATE("DMA state S0\n"); + if (m_m6844_channel[m_current_channel].active == 1 && + m_m6844_channel[m_current_channel].counter != 0) + { + m_state = STATE_S1; + } + else + { + LOGSTATE("Suspend in S0\n"); + suspend_until_trigger(1, true); + m_icount = 0; + } + break; + case STATE_S1: // Wait for Tx RQ == 1 + LOGSTATE("DMA state S1\n"); + if (m_dreq[m_current_channel] == ASSERT_LINE) + { + m_state = STATE_S2; + switch(m_m6844_channel[m_current_channel].control & 0x06) + { + case 0x00: // Mode 2 - single-byte transfer HALT steal mode + case 0x02: // Mode 3 - block transfer mode + m_out_drq2_cb(ASSERT_LINE); + break; + case 0x04: // Mode 1 - single-byte transfer TSC steal mode + m_out_drq1_cb(ASSERT_LINE); + break; + default: + m_out_drq1_cb(CLEAR_LINE); + m_out_drq2_cb(CLEAR_LINE); + break; + } + } + else + { + LOGSTATE("Suspend in S1\n"); + suspend_until_trigger(1, true); + m_icount = 0; + } + break; + case STATE_S2: // Wait for DGRNT == 1 + LOGSTATE("DMA state S2\n"); + if (m_dgrnt == ASSERT_LINE && m_dreq[m_current_channel] == ASSERT_LINE) + { + m_out_txak_cb(m_current_channel); + + if (m_m6844_channel[m_current_channel].active == 1) //active dma transfer + { + if (!(m_m6844_channel[m_current_channel].control & 0x01)) + { + //uint8_t data = 0x55; + uint8_t data = m_in_ior_cb[m_current_channel](); + LOGTFR("DMA%d from device to memory location %04x: <- %02x\n", m_current_channel, m_m6844_channel[m_current_channel].address, data ); + m_out_memw_cb(m_m6844_channel[m_current_channel].address, data); + } + else // dma write to device from memory + { + uint8_t data = 0; + LOGTFR("DMA from memory location to device %04x: -> %02x\n", m_m6844_channel[m_current_channel].address, data ); + //uint8_t data = m_in_memr_cb(m_m6844_channel[m_current_channel].address); + //m_out_iow_cb[m_current_channel](data); + } + + if (m_m6844_channel[m_current_channel].control & 0x08) + { + m_m6844_channel[m_current_channel].address--; + } + else + { + m_m6844_channel[m_current_channel].address++; + } + + m_m6844_channel[m_current_channel].counter--; + + if (m_m6844_channel[m_current_channel].counter == 0) + { + m_out_drq1_cb(CLEAR_LINE); + m_out_drq2_cb(CLEAR_LINE); + m_m6844_channel[m_current_channel].control |= 0x80; + m6844_update_interrupt(); + m_state = STATE_SI; + } + else + { + switch(m_m6844_channel[m_current_channel].control & 0x06) + { + case 0x00: + LOGTFR("Mode 2 - single-byte transfer HALT steal mode\n"); + m_state = STATE_S1; + m_out_drq2_cb(CLEAR_LINE); + break; + case 0x02: + LOGTFR("Mode 3 - block transfer mode\n"); + m_state = STATE_S2; // Just for clarity, we are still in STATE_S2 + break; + case 0x04: + LOGTFR("Mode 1 - single-byte transfer TSC steal mode\n"); + m_state = STATE_S1; + m_out_drq1_cb(CLEAR_LINE); + break; + default: // Undefined - needs verification on real hardware + logerror("MC6844: undefined transfer mode, clearing DMA request\n"); + m_state = STATE_SI; + m_out_drq1_cb(CLEAR_LINE); + m_out_drq2_cb(CLEAR_LINE); + break; + } + } + } + } + else + { + LOGSTATE("Suspend in S2\n"); + suspend_until_trigger(1, true); + m_icount = 0; + } + break; + default: + logerror("MC6844: bad state, please report error\n"); + break; + } + + m_icount--; + } while (m_icount > 0); +} + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +//------------------------------------------------- +// read handler +//------------------------------------------------- + +uint8_t mc6844_device::read(offs_t offset) +{ + uint8_t result = 0; + + // switch off the offset we were given + switch (offset) + { + // upper byte of address + case 0x00: + case 0x04: + case 0x08: + case 0x0c: + result = m_m6844_channel[offset / 4].address >> 8; + break; + + // lower byte of address + case 0x01: + case 0x05: + case 0x09: + case 0x0d: + result = m_m6844_channel[offset / 4].address & 0xff; + break; + + // upper byte of counter + case 0x02: + case 0x06: + case 0x0a: + case 0x0e: + result = m_m6844_channel[offset / 4].counter >> 8; + break; + + // lower byte of counter + case 0x03: + case 0x07: + case 0x0b: + case 0x0f: + result = m_m6844_channel[offset / 4].counter & 0xff; + break; + + // channel control + case 0x10: + case 0x11: + case 0x12: + case 0x13: + result = m_m6844_channel[offset - 0x10].control; + + // A read here clears the 'DMA end' flag of the + // associated channel. + if (!machine().side_effects_disabled()) + { + m_m6844_channel[offset - 0x10].control &= ~0x80; + if (m_m6844_interrupt & 0x80) + m6844_update_interrupt(); + } + break; + + // priority control + case 0x14: + result = m_m6844_priority; + break; + + // interrupt control + case 0x15: + result = m_m6844_interrupt; + break; + + // chaining control + case 0x16: + result = m_m6844_chain; + break; + + // 0x17-0x1f not used + default: break; + } + + return result & 0xff; +} + +//------------------------------------------------- +// write() handler +//------------------------------------------------- + +void mc6844_device::write(offs_t offset, uint8_t data) +{ + int i; + LOGSETUP("DMA write %02x: %02x\n", offset, data); + // switch off the offset we were given + switch (offset) + { + // upper byte of address + case 0x00: + case 0x04: + case 0x08: + case 0x0c: + LOGSETUP(" - upper address byte ch %d: %02x\n", offset / 4, data); + m_m6844_channel[offset / 4].address = (m_m6844_channel[offset / 4].address & 0xff) | (data << 8); + break; + + // lower byte of address + case 0x01: + case 0x05: + case 0x09: + case 0x0d: + LOGSETUP(" - lower address byte ch %d: %02x\n", offset / 4, data); + m_m6844_channel[offset / 4].address = (m_m6844_channel[offset / 4].address & 0xff00) | (data & 0xff); + break; + + // upper byte of counter + case 0x02: + case 0x06: + case 0x0a: + case 0x0e: + LOGSETUP(" - upper counter byte ch %d: %02x\n", offset / 4, data); + m_m6844_channel[offset / 4].counter = (m_m6844_channel[offset / 4].counter & 0xff) | (data << 8); + break; + + // lower byte of counter + case 0x03: + case 0x07: + case 0x0b: + case 0x0f: + LOGSETUP(" - lower counter byte ch %d: %02x\n", offset / 4, data); + m_m6844_channel[offset / 4].counter = (m_m6844_channel[offset / 4].counter & 0xff00) | (data & 0xff); + break; + + // channel control + case 0x10: + case 0x11: + case 0x12: + case 0x13: + LOGSETUP(" - control byte ch %d: %02x\n", offset / 4, data); + m_m6844_channel[offset - 0x10].control = (m_m6844_channel[offset - 0x10].control & 0xc0) | (data & 0x3f); + break; + + // priority control + case 0x14: + LOGSETUP(" - priority byte: %02x\n", data); + m_m6844_priority = data; + + // update each channel + for (i = 0; i < 4; i++) + { + // if we're going active... + if (!m_m6844_channel[i].active && (data & (1 << i))) + { + // mark us active + m_m6844_channel[i].active = 1; + + // set the DMA busy bit and clear the DMA end bit + m_m6844_channel[i].control |= 0x40; + m_m6844_channel[i].control &= ~0x80; + + // set the starting address, counter, and time + m_m6844_channel[i].start_address = m_m6844_channel[i].address; + m_m6844_channel[i].start_counter = m_m6844_channel[i].counter; + } + + // if we're going inactive... + else if (m_m6844_channel[i].active && !(data & (1 << i))) + { + //mark us inactive + m_m6844_channel[i].active = 0; + } + } + break; + + // interrupt control + case 0x15: + LOGSETUP(" - interrupt control: %02x\n", data); + m_m6844_interrupt = (m_m6844_interrupt & 0x80) | (data & 0x7f); + m6844_update_interrupt(); + break; + + // chaining control + case 0x16: + LOGSETUP(" - chaining control: %02x\n", data); + m_m6844_chain = data; + break; + + // 0x17-0x1f not used + default: break; + } + LOGSTATE("Trigger(1)\n"); + trigger(1); +} + +//------------------------------------------------- +// m6844_update_interrupt() +//------------------------------------------------- + +void mc6844_device::m6844_update_interrupt() +{ + uint8_t interrupt = 0; + + interrupt |= BIT(m_m6844_channel[0].control, 7) & BIT(m_m6844_interrupt, 0); + interrupt |= BIT(m_m6844_channel[1].control, 7) & BIT(m_m6844_interrupt, 1); + interrupt |= BIT(m_m6844_channel[2].control, 7) & BIT(m_m6844_interrupt, 2); + interrupt |= BIT(m_m6844_channel[3].control, 7) & BIT(m_m6844_interrupt, 3); + + if (interrupt) + { + if (!(m_m6844_interrupt & 0x80)) + { + // Set interrupt indication bit 7. + m_m6844_interrupt |= 0x80; + m_out_int_cb(ASSERT_LINE); + } + } + else + { + if (m_m6844_interrupt & 0x80) + { + // Clear interrupt indication bit 7. + m_m6844_interrupt &= 0x7f; + m_out_int_cb(CLEAR_LINE); + } + } +} diff --git a/src/devices/machine/mc6844.h b/src/devices/machine/mc6844.h new file mode 100644 index 00000000000..e2db601e8e7 --- /dev/null +++ b/src/devices/machine/mc6844.h @@ -0,0 +1,148 @@ +// license:BSD-3-Clause +// copyright-holders: Joakim Larsson Edström +/*************************************************************************** + + Motorola 6844 emulation + +**************************************************************************** + _____ _____ + VSS 1 |* \_/ | 40 E + *CS / Tx AKBW 2 | | 39 *RESET + R / *W 3 | | 38 DGRNT + A0 4 | | 37 *DRQ1 + A1 5 | | 36 *DRQ2 + A2 6 | | 35 Tx AKA + A3 7 | | 34 *TX STB + A4 8 | | 33 *IRQ / *DEND + A5 9 | | 32 Tx RQ0 + A6 10 | MC6844 | 31 Tx RQ1 + A7 11 | | 30 Tx RQ2 + A8 12 | | 29 Tx RQ3 + A9 13 | | 28 D0 + A10 14 | | 27 D1 + A11 15 | | 26 D2 + A12 16 | | 25 D3 + A13 17 | | 24 D4 + A14 18 | | 23 D5 + A15 19 | | 22 D6 + VCC 20 |_____________| 21 D7 + +***************************************************************************/ + +#ifndef MAME_MACHINE_MC6844_H +#define MAME_MACHINE_MC6844_H + +#pragma once + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> mc6844_device + +class mc6844_device : public device_t, public device_execute_interface +{ +public: + // construction/destruction + mc6844_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + auto out_int_callback() { return m_out_int_cb.bind(); } + auto out_txak_callback() { return m_out_txak_cb.bind(); } + auto out_drq1_callback() { return m_out_drq1_cb.bind(); } + auto out_drq2_callback() { return m_out_drq2_cb.bind(); } + auto in_memr_callback() { return m_in_memr_cb.bind(); } + auto out_memw_callback() { return m_out_memw_cb.bind(); } + + // I/O operations + void write(offs_t offset, uint8_t data); + uint8_t read(offs_t offset); + template <unsigned CH> auto in_ior_callback() { return m_in_ior_cb[CH].bind(); } + template <unsigned CH> auto out_iow_callback() { return m_out_iow_cb[CH].bind(); } + + template <unsigned CH> DECLARE_WRITE_LINE_MEMBER( dreq_w ) { dma_request(CH, state); } + + DECLARE_WRITE_LINE_MEMBER( dgrnt_w ){ m_dgrnt = state; trigger(1); } + +protected: + // device-level overrides + //virtual void device_validity_check(validity_checker &valid) const override; + virtual void device_resolve_objects() override; + virtual void device_start() override; + virtual void device_reset() override; + virtual void execute_run() override; + + devcb_write_line m_out_int_cb; + devcb_write8 m_out_txak_cb; + devcb_write_line m_out_drq1_cb; + devcb_write_line m_out_drq2_cb; + devcb_read8 m_in_memr_cb; + devcb_write8 m_out_memw_cb; + devcb_read8 m_in_ior_cb[4]; + devcb_write8 m_out_iow_cb[4]; + + /* channel_data structure holds info about each 6844 DMA channel */ + struct m6844_channel_data + { + int active; + int address; + int counter; + // Channel control register. + // bit 0: Read / Write mode + // bit 1: Mode control B + // bit 2: Mode control A + // bit 3: Address up (0) / down (1). + // bit 4: Not used + // bit 5: Not used + // bit 6: Busy / Ready. Read only. Set when request + // made. Cleared when transfer completed. + // bit 7: DMA end flag. Read only? Set when transfer + // completed. Cleared when control register + // read. Sets IRQ. + // Mode control A,B: 0,0 Mode2; 0,1 Mode 3; 1,0 Mode 0; + // 1,1 Undefined. + uint8_t control; + int start_address; + int start_counter; + }; + + /* 6844 description */ + m6844_channel_data m_m6844_channel[4]; + uint8_t m_m6844_priority; + // Interrupt control register. + // Bit 0-3: channel interrupt enable, 1 enabled, 0 masked. + // Bit 4-6: unused + // Bit 7: Read only. Set to 1 when IRQ asserted. Clear when the + // control register associated with the channel that caused the + // interrut is read. + uint8_t m_m6844_interrupt; + uint8_t m_m6844_chain; + void m6844_update_interrupt(); + + // State machine + enum { + STATE_SI, + STATE_S0, + STATE_S1, + STATE_S2 + }; + + int m_state; + int m_icount; + int m_current_channel; + int m_last_channel; + + // input states + bool m_dgrnt; + bool m_dreq[4]; +private: + void dma_request(int channel, int state); +}; + +// device type definition +DECLARE_DEVICE_TYPE(MC6844, mc6844_device) + +#endif // MAME_MACHINE_MC6844_H |