summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/mc6844.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/mc6844.cpp')
-rw-r--r--src/devices/machine/mc6844.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/devices/machine/mc6844.cpp b/src/devices/machine/mc6844.cpp
new file mode 100644
index 00000000000..c5fdb21c870
--- /dev/null
+++ b/src/devices/machine/mc6844.cpp
@@ -0,0 +1,561 @@
+// 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));
+ save_item(NAME(m_dreq));
+
+ save_item(STRUCT_MEMBER( m_m6844_channel, active));
+ save_item(STRUCT_MEMBER( m_m6844_channel, address));
+ save_item(STRUCT_MEMBER( m_m6844_channel, counter));
+ save_item(STRUCT_MEMBER( m_m6844_channel, control));
+ save_item(STRUCT_MEMBER( m_m6844_channel, start_address));
+ save_item(STRUCT_MEMBER( m_m6844_channel, start_counter));
+}
+
+//-------------------------------------------------
+// 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 = 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;
+ //uint8_t data = m_in_memr_cb(m_m6844_channel[m_current_channel].address);
+ LOGTFR("DMA from memory location to device %04x: -> %02x\n", m_m6844_channel[m_current_channel].address, data );
+ //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);
+ }
+ }
+}