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