1 // license:BSD-3-Clause
2 // copyright-holders: Joakim Larsson Edström
3 /**********************************************************************
4 
5   Motorola 6844 emulation. This code is not yet ready for general use, see TODO list below
6 
7  "MC6844 — Direct Memory Access Controller
8 
9   This DMAC works with an M6800 MPU Clock Pulse Generator and an I/O Peripheral Controller,
10   such as the units described here, to facilitate  direct access to the computer memory by
11   the peripheral, thus by passing MPU interactive time delay.
12 
13   General Description
14 
15   The MC6844 is operable in three modes: HALT Burst, Cycle Steal and TSC Steal.
16   In the Burst Mode, the MPU is halted by the first transfer request (TxRQ) input and
17   is restarted when the Byte Count Register (BCR) is zero. Each data transfer is synchronized
18   by a pulse input of TxRQ. In the Cycle Steal Mode, the MPU is halted by each TxRQ and
19   is restarted after each one byte of data transferred. In the TSC Steal Mode, DMAC uses the
20   three-state control function of the MPU to control the system bus. One byte of data is
21   transferred during each DMA cycle.
22 
23   The DMAC has four channels. A Priority Control Register determines which of the channels
24   is enabled. While data is being transferred on one channel, the other channels are inhibited.
25   When one channel completes transferring, the next will become valid for DMA transfer. The PCR
26   also utilizes a Rotate Control bit. Priority of DMA transfer is normally fixed in sequential
27   order. The highest priority is in #0 Channel and the lowest is in #3. When this bit is in high
28   level, channel priority is rotated such that the just-serviced channel has the lowest priority
29   in the next DMA transfer."
30 
31   Source: https://en.wikipedia.org/wiki/File:Motorola_Microcomputer_Components_1978_pg13.jpg
32 
33   CREDITS & Prior Work:
34   The base code was ripped out of swtpc09.cpp and deviceified but similar code is also to be found
35   in exidy440.cpp so copyrigt is probably shared among the authors there: Robert Justice, 68bit and
36   Aaron Giles.
37 
38   TODO:
39   - Memory to Device transfers
40 
41 **********************************************************************/
42 
43 #include "emu.h"
44 #include "mc6844.h"
45 
46 //**************************************************************************
47 //  GLOBAL VARIABLES
48 //**************************************************************************
49 #define LOG_SETUP   (1U << 1)
50 #define LOG_INT     (1U << 2)
51 #define LOG_STATE   (1U << 3)
52 #define LOG_TFR     (1U << 4)
53 
54 //#define VERBOSE (LOG_GENERAL|LOG_SETUP | LOG_INT | LOG_STATE |LOG_TFR)
55 //#define LOG_OUTPUT_STREAM std::cout
56 
57 #include "logmacro.h"
58 
59 #define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
60 #define LOGINT(...)   LOGMASKED(LOG_INT,   __VA_ARGS__)
61 #define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__)
62 #define LOGTFR(...)   LOGMASKED(LOG_TFR,   __VA_ARGS__)
63 
64 #ifdef _MSC_VER
65 #define FUNCNAME __func__
66 #else
67 #define FUNCNAME __PRETTY_FUNCTION__
68 #endif
69 
70 // device type definition
71 DEFINE_DEVICE_TYPE(MC6844, mc6844_device, "mc6844", "MC6844 DMA")
72 
73 //**************************************************************************
74 //  LIVE DEVICE
75 //**************************************************************************
76 
77 //-------------------------------------------------
78 //  mc6844_device - constructor
79 //-------------------------------------------------
mc6844_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)80 mc6844_device::mc6844_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
81 	: device_t(mconfig, MC6844, tag, owner, clock)
82 	, device_execute_interface(mconfig, *this)
83 	, m_out_int_cb(*this)
84 	, m_out_txak_cb(*this)
85 	, m_out_drq1_cb(*this)
86 	, m_out_drq2_cb(*this)
87 	, m_in_memr_cb(*this)
88 	, m_out_memw_cb(*this)
89 	, m_in_ior_cb(*this)
90 	, m_out_iow_cb(*this)
91 	, m_state(STATE_S0)
92 	, m_icount(0)
93 {
94 }
95 
96 //-------------------------------------------------
97 //  device_resolve_objects - device-specific setup
98 //-------------------------------------------------
device_resolve_objects()99 void mc6844_device::device_resolve_objects()
100 {
101 	m_out_int_cb.resolve_safe();
102 	m_out_txak_cb.resolve_safe();
103 	m_out_drq1_cb.resolve_safe();
104 	m_out_drq2_cb.resolve_safe();
105 	m_in_memr_cb.resolve_safe(0);
106 	m_out_memw_cb.resolve_safe();
107 
108 	m_in_ior_cb.resolve_all_safe(0);
109 	m_out_iow_cb.resolve_all_safe();
110 }
111 
112 //-------------------------------------------------
113 //  device_start - device-specific startup
114 //-------------------------------------------------
115 
device_start()116 void mc6844_device::device_start()
117 {
118 	// set our instruction counter
119 	set_icountptr(m_icount);
120 
121 	save_item(NAME(m_m6844_priority));
122 	save_item(NAME(m_m6844_interrupt));
123 	save_item(NAME(m_m6844_chain));
124 	save_item(NAME(m_state));
125 	save_item(NAME(m_icount));
126 	save_item(NAME(m_current_channel));
127 	save_item(NAME(m_last_channel));
128 	save_item(NAME(m_dgrnt));
129 }
130 
131 //-------------------------------------------------
132 //  device_reset - device-specific reset
133 //-------------------------------------------------
134 
device_reset()135 void mc6844_device::device_reset()
136 {
137 	// reset the 6844
138 	for (int i = 0; i < 4; i++)
139 	{
140 		m_m6844_channel[i].active = 0;
141 		m_m6844_channel[i].control = 0x00;
142 	}
143 	m_m6844_priority = 0x00;
144 	m_m6844_interrupt = 0x00;
145 	m_m6844_chain = 0x00;
146 	m_state = STATE_SI;
147 }
148 
149 //-------------------------------------------------
150 //  dma_request -
151 //-------------------------------------------------
152 
dma_request(int channel,int state)153 void mc6844_device::dma_request(int channel, int state)
154 {
155 	LOG("MC6844 Channel %u DMA Request: %u\n", channel, state);
156 
157 	m_dreq[channel & 3] = state;
158 
159 	LOGSTATE("Trigger(1)\n");
160 	trigger(1);
161 }
162 
163 //-------------------------------------------------
164 //  execute_run -
165 //-------------------------------------------------
166 
execute_run()167 void mc6844_device::execute_run()
168 {
169 	do
170 	{
171 		switch (m_state)
172 		{
173 		case STATE_SI:  // IDLE state, will suspend until a DMA request comes through
174 			{   //                    Hi ------> Lo
175 				int const priorities[][4] = {{ 1, 2, 3, 0 },
176 							   { 2, 3, 0, 1 },
177 							   { 3, 0, 1, 2 },
178 							   { 0, 1, 2, 3 }};
179 
180 				LOGSTATE("DMA state SI\n");
181 				for (int prio = 0; prio < 4; prio++)
182 				{
183 					// Rotating or static channel prioritizations
184 					int current_channel = priorities[((m_m6844_priority & 0x80) ? m_last_channel : 3) & 3][prio];
185 
186 				if (m_m6844_channel[current_channel].active == 1 && m_dreq[current_channel] == ASSERT_LINE)
187 					{
188 						m_current_channel = m_last_channel = current_channel;
189 						m_state = STATE_S0;
190 						break;
191 					}
192 				}
193 			}
194 			if(m_state == STATE_SI)
195 			{
196 				LOGSTATE("Suspend in SI\n");
197 				suspend_until_trigger(1, true);
198 				m_icount = 0;
199 			}
200 			break;
201 		case STATE_S0: // Wait for BCR != 0 and Tx EN == 1
202 			LOGSTATE("DMA state S0\n");
203 			if (m_m6844_channel[m_current_channel].active == 1 &&
204 				m_m6844_channel[m_current_channel].counter != 0)
205 			{
206 				m_state = STATE_S1;
207 			}
208 			else
209 			{
210 				LOGSTATE("Suspend in S0\n");
211 				suspend_until_trigger(1, true);
212 				m_icount = 0;
213 			}
214 			break;
215 		case STATE_S1: // Wait for Tx RQ == 1
216 			LOGSTATE("DMA state S1\n");
217 			if (m_dreq[m_current_channel] == ASSERT_LINE)
218 			{
219 				m_state = STATE_S2;
220 				switch(m_m6844_channel[m_current_channel].control & 0x06)
221 				{
222 				case 0x00: // Mode 2 - single-byte transfer HALT steal mode
223 				case 0x02: // Mode 3 - block transfer mode
224 					m_out_drq2_cb(ASSERT_LINE);
225 					break;
226 				case 0x04: // Mode 1 - single-byte transfer TSC steal mode
227 					m_out_drq1_cb(ASSERT_LINE);
228 					break;
229 				default:
230 					m_out_drq1_cb(CLEAR_LINE);
231 					m_out_drq2_cb(CLEAR_LINE);
232 					break;
233 				}
234 			}
235 			else
236 			{
237 				LOGSTATE("Suspend in S1\n");
238 				suspend_until_trigger(1, true);
239 				m_icount = 0;
240 			}
241 			break;
242 		case STATE_S2: // Wait for DGRNT == 1
243 			LOGSTATE("DMA state S2\n");
244 			if (m_dgrnt == ASSERT_LINE && m_dreq[m_current_channel] == ASSERT_LINE)
245 			{
246 				m_out_txak_cb(m_current_channel);
247 
248 				if (m_m6844_channel[m_current_channel].active == 1)  //active dma transfer
249 				{
250 					if (!(m_m6844_channel[m_current_channel].control & 0x01))
251 					{
252 						//uint8_t data = 0x55;
253 						uint8_t data = m_in_ior_cb[m_current_channel]();
254 						LOGTFR("DMA%d from device to memory location %04x: <- %02x\n", m_current_channel, m_m6844_channel[m_current_channel].address, data );
255 						m_out_memw_cb(m_m6844_channel[m_current_channel].address, data);
256 					}
257 					else // dma write to device from memory
258 					{
259 						uint8_t data = 0;
260 						LOGTFR("DMA from memory location to device %04x: -> %02x\n", m_m6844_channel[m_current_channel].address, data );
261 						//uint8_t data = m_in_memr_cb(m_m6844_channel[m_current_channel].address);
262 						//m_out_iow_cb[m_current_channel](data);
263 					}
264 
265 					if (m_m6844_channel[m_current_channel].control & 0x08)
266 					{
267 						m_m6844_channel[m_current_channel].address--;
268 					}
269 					else
270 					{
271 						m_m6844_channel[m_current_channel].address++;
272 					}
273 
274 					m_m6844_channel[m_current_channel].counter--;
275 
276 					if (m_m6844_channel[m_current_channel].counter == 0)
277 					{
278 						m_out_drq1_cb(CLEAR_LINE);
279 						m_out_drq2_cb(CLEAR_LINE);
280 						m_m6844_channel[m_current_channel].control |= 0x80;
281 						m6844_update_interrupt();
282 						m_state = STATE_SI;
283 					}
284 					else
285 					{
286 						switch(m_m6844_channel[m_current_channel].control & 0x06)
287 						{
288 						case 0x00:
289 							LOGTFR("Mode 2 - single-byte transfer HALT steal mode\n");
290 							m_state = STATE_S1;
291 							m_out_drq2_cb(CLEAR_LINE);
292 							break;
293 						case 0x02:
294 							LOGTFR("Mode 3 - block transfer mode\n");
295 							m_state = STATE_S2; // Just for clarity, we are still in STATE_S2
296 							break;
297 						case 0x04:
298 							LOGTFR("Mode 1 - single-byte transfer TSC steal mode\n");
299 							m_state = STATE_S1;
300 							m_out_drq1_cb(CLEAR_LINE);
301 							break;
302 						default: // Undefined - needs verification on real hardware
303 							logerror("MC6844: undefined transfer mode, clearing DMA request\n");
304 							m_state = STATE_SI;
305 							m_out_drq1_cb(CLEAR_LINE);
306 							m_out_drq2_cb(CLEAR_LINE);
307 							break;
308 						}
309 					}
310 				}
311 			}
312 			else
313 			{
314 				LOGSTATE("Suspend in S2\n");
315 				suspend_until_trigger(1, true);
316 				m_icount = 0;
317 			}
318 			break;
319 		default:
320 			logerror("MC6844: bad state, please report error\n");
321 			break;
322 		}
323 
324 		m_icount--;
325 	} while (m_icount > 0);
326 }
327 
328 //**************************************************************************
329 //  READ/WRITE HANDLERS
330 //**************************************************************************
331 
332 //-------------------------------------------------
333 //  read handler
334 //-------------------------------------------------
335 
read(offs_t offset)336 uint8_t mc6844_device::read(offs_t offset)
337 {
338 	uint8_t result = 0;
339 
340 	// switch off the offset we were given
341 	switch (offset)
342 	{
343 		// upper byte of address
344 		case 0x00:
345 		case 0x04:
346 		case 0x08:
347 		case 0x0c:
348 			result = m_m6844_channel[offset / 4].address >> 8;
349 			break;
350 
351 		// lower byte of address
352 		case 0x01:
353 		case 0x05:
354 		case 0x09:
355 		case 0x0d:
356 			result = m_m6844_channel[offset / 4].address & 0xff;
357 			break;
358 
359 		// upper byte of counter
360 		case 0x02:
361 		case 0x06:
362 		case 0x0a:
363 		case 0x0e:
364 			result = m_m6844_channel[offset / 4].counter >> 8;
365 			break;
366 
367 		// lower byte of counter
368 		case 0x03:
369 		case 0x07:
370 		case 0x0b:
371 		case 0x0f:
372 			result = m_m6844_channel[offset / 4].counter & 0xff;
373 			break;
374 
375 		// channel control
376 		case 0x10:
377 		case 0x11:
378 		case 0x12:
379 		case 0x13:
380 			result = m_m6844_channel[offset - 0x10].control;
381 
382 			// A read here clears the 'DMA end' flag of the
383 			// associated channel.
384 			if (!machine().side_effects_disabled())
385 			{
386 				m_m6844_channel[offset - 0x10].control &= ~0x80;
387 				if (m_m6844_interrupt & 0x80)
388 					m6844_update_interrupt();
389 			}
390 			break;
391 
392 		// priority control
393 		case 0x14:
394 			result = m_m6844_priority;
395 			break;
396 
397 		// interrupt control
398 		case 0x15:
399 			result = m_m6844_interrupt;
400 			break;
401 
402 		// chaining control
403 		case 0x16:
404 			result = m_m6844_chain;
405 			break;
406 
407 		// 0x17-0x1f not used
408 		default: break;
409 	}
410 
411 	return result & 0xff;
412 }
413 
414 //-------------------------------------------------
415 //  write() handler
416 //-------------------------------------------------
417 
write(offs_t offset,uint8_t data)418 void mc6844_device::write(offs_t offset, uint8_t data)
419 {
420 	int i;
421 	LOGSETUP("DMA write %02x: %02x\n", offset, data);
422 	// switch off the offset we were given
423 	switch (offset)
424 	{
425 		// upper byte of address
426 		case 0x00:
427 		case 0x04:
428 		case 0x08:
429 		case 0x0c:
430 			LOGSETUP(" - upper address byte ch %d: %02x\n", offset / 4, data);
431 			m_m6844_channel[offset / 4].address = (m_m6844_channel[offset / 4].address & 0xff) | (data << 8);
432 			break;
433 
434 		// lower byte of address
435 		case 0x01:
436 		case 0x05:
437 		case 0x09:
438 		case 0x0d:
439 			LOGSETUP(" - lower address byte ch %d: %02x\n", offset / 4, data);
440 			m_m6844_channel[offset / 4].address = (m_m6844_channel[offset / 4].address & 0xff00) | (data & 0xff);
441 			break;
442 
443 		// upper byte of counter
444 		case 0x02:
445 		case 0x06:
446 		case 0x0a:
447 		case 0x0e:
448 			LOGSETUP(" - upper counter byte ch %d: %02x\n", offset / 4, data);
449 			m_m6844_channel[offset / 4].counter = (m_m6844_channel[offset / 4].counter & 0xff) | (data << 8);
450 			break;
451 
452 		// lower byte of counter
453 		case 0x03:
454 		case 0x07:
455 		case 0x0b:
456 		case 0x0f:
457 			LOGSETUP(" - lower counter byte ch %d: %02x\n", offset / 4, data);
458 			m_m6844_channel[offset / 4].counter = (m_m6844_channel[offset / 4].counter & 0xff00) | (data & 0xff);
459 			break;
460 
461 		// channel control
462 		case 0x10:
463 		case 0x11:
464 		case 0x12:
465 		case 0x13:
466 			LOGSETUP(" - control byte ch %d: %02x\n", offset / 4, data);
467 			m_m6844_channel[offset - 0x10].control = (m_m6844_channel[offset - 0x10].control & 0xc0) | (data & 0x3f);
468 			break;
469 
470 		// priority control
471 		case 0x14:
472 			LOGSETUP(" - priority byte: %02x\n", data);
473 			m_m6844_priority = data;
474 
475 			// update each channel
476 			for (i = 0; i < 4; i++)
477 			{
478 				// if we're going active...
479 				if (!m_m6844_channel[i].active && (data & (1 << i)))
480 				{
481 					// mark us active
482 					m_m6844_channel[i].active = 1;
483 
484 					// set the DMA busy bit and clear the DMA end bit
485 					m_m6844_channel[i].control |= 0x40;
486 					m_m6844_channel[i].control &= ~0x80;
487 
488 					// set the starting address, counter, and time
489 					m_m6844_channel[i].start_address = m_m6844_channel[i].address;
490 					m_m6844_channel[i].start_counter = m_m6844_channel[i].counter;
491 				}
492 
493 				// if we're going inactive...
494 				else if (m_m6844_channel[i].active && !(data & (1 << i)))
495 				{
496 					//mark us inactive
497 					m_m6844_channel[i].active = 0;
498 				}
499 			}
500 			break;
501 
502 		// interrupt control
503 		case 0x15:
504 			LOGSETUP(" - interrupt control: %02x\n", data);
505 			m_m6844_interrupt = (m_m6844_interrupt & 0x80) | (data & 0x7f);
506 			m6844_update_interrupt();
507 			break;
508 
509 		// chaining control
510 		case 0x16:
511 			LOGSETUP(" - chaining control: %02x\n", data);
512 			m_m6844_chain = data;
513 			break;
514 
515 		// 0x17-0x1f not used
516 		default: break;
517 	}
518 	LOGSTATE("Trigger(1)\n");
519 	trigger(1);
520 }
521 
522 //-------------------------------------------------
523 //  m6844_update_interrupt()
524 //-------------------------------------------------
525 
m6844_update_interrupt()526 void mc6844_device::m6844_update_interrupt()
527 {
528 	uint8_t interrupt = 0;
529 
530 	interrupt |= BIT(m_m6844_channel[0].control, 7) & BIT(m_m6844_interrupt, 0);
531 	interrupt |= BIT(m_m6844_channel[1].control, 7) & BIT(m_m6844_interrupt, 1);
532 	interrupt |= BIT(m_m6844_channel[2].control, 7) & BIT(m_m6844_interrupt, 2);
533 	interrupt |= BIT(m_m6844_channel[3].control, 7) & BIT(m_m6844_interrupt, 3);
534 
535 	if (interrupt)
536 	{
537 		if (!(m_m6844_interrupt & 0x80))
538 		{
539 			// Set interrupt indication bit 7.
540 			m_m6844_interrupt |= 0x80;
541 			m_out_int_cb(ASSERT_LINE);
542 		}
543 	}
544 	else
545 	{
546 		if (m_m6844_interrupt & 0x80)
547 		{
548 			// Clear interrupt indication bit 7.
549 			m_m6844_interrupt &= 0x7f;
550 			m_out_int_cb(CLEAR_LINE);
551 		}
552 	}
553 }
554