1 /**********************************************************************
2
3 8257 DMA interface and emulation
4
5 For datasheet http://www.threedee.com/jcm/library/index.html
6
7 2008/05 Miodrag Milanovic
8
9 - added support for autoload mode
10 - fixed bug in calculating count
11
12 2007/11 couriersud
13
14 - architecture copied from 8237 DMA
15 - significant changes to implementation
16
17 The DMA works like this:
18
19 1. The device asserts the DRQn line
20 2. The DMA clears the TC (terminal count) line
21 3. The DMA asserts the CPU's HRQ (halt request) line
22 4. Upon acknowledgement of the halt, the DMA will let the device
23 know that it needs to send information by asserting the DACKn
24 line
25 5. The DMA will read the byte from the device
26 6. The device clears the DRQn line
27 7. The DMA clears the CPU's HRQ line
28 8. (steps 3-7 are repeated for every byte in the chain)
29
30 MAME sources by Curt Coder,Carl
31
32 **********************************************************************/
33
34 #include "burnint.h"
35 #include "driver.h"
36 #include "8257dma.h"
37
38 #define I8257_NUM_CHANNELS (4)
39
40 #define I8257_STATUS_UPDATE 0x10
41 #define I8257_STATUS_TC_CH3 0x08
42 #define I8257_STATUS_TC_CH2 0x04
43 #define I8257_STATUS_TC_CH1 0x02
44 #define I8257_STATUS_TC_CH0 0x01
45
46 #define DMA_MODE_AUTOLOAD(mode) ((mode) & 0x80)
47 #define DMA_MODE_TCSTOP(mode) ((mode) & 0x40)
48 #define DMA_MODE_EXWRITE(mode) ((mode) & 0x20)
49 #define DMA_MODE_ROTPRIO(mode) ((mode) & 0x10)
50 #define DMA_MODE_CH_EN(mode, chan) ((mode) & (1 << (chan)))
51
52 #define TIMER_OPERATION 0
53 #define TIMER_MSBFLIP 1
54 #define TIMER_DRQ_SYNC 2
55
56 static void (*m_out_hrq_func)(INT32 line); // halt
57 static void (*m_out_tc_func)(INT32 line);
58 static void (*m_out_mark_func)(INT32 line);
59
60 static UINT8 (*m_in_memr_func)(UINT16 address);
61 static void (*m_out_memw_func)(UINT16 address, UINT8 data);
62 static ior_in_functs m_in_ior_func[I8257_NUM_CHANNELS];
63 static ior_out_functs m_out_iow_func[I8257_NUM_CHANNELS];
64 static INT32 (*m_idle_func)(INT32);
65
66 static UINT16 m_registers[I8257_NUM_CHANNELS*2];
67 static UINT16 m_address[I8257_NUM_CHANNELS];
68 static UINT16 m_count[I8257_NUM_CHANNELS];
69 static UINT8 m_rwmode[I8257_NUM_CHANNELS];
70 static UINT8 m_mode;
71 static UINT8 m_rr;
72 static UINT8 m_msb;
73 static UINT8 m_drq;
74 static UINT8 m_status; /* bits 0- 3 : Terminal count for channels 0-3 */
75
76 void i8257_update_status();
77
78 //-------------------------------------------------
79 // device_start - device-specific startup
80 //-------------------------------------------------
81
82 // fake functions to keep everything safe
null_line(INT32)83 static void null_line(INT32){}
null_in(UINT16)84 static UINT8 null_in(UINT16){ return 0; }
null_out(UINT16,UINT8)85 static void null_out(UINT16,UINT8){}
null_idle(INT32)86 static INT32 null_idle(INT32){return 0;}
87
88 static INT32 trigger_transfer = 0;
89
i8257Init()90 void i8257Init()
91 {
92 DebugDev_8257DMAInitted = 1;
93
94 // these aren't used atm.
95 m_out_hrq_func = null_line;
96 m_out_tc_func = null_line;
97 m_out_mark_func = null_line;
98
99 m_in_memr_func = null_in;
100 m_out_memw_func = null_out;
101
102 m_idle_func = null_idle;
103
104 for (INT32 i = 0; i < I8257_NUM_CHANNELS; i++) {
105 m_in_ior_func[i] = null_in;
106 m_out_iow_func[i] = null_out;
107 }
108 }
109
i8257Exit()110 void i8257Exit()
111 {
112 #if defined FBNEO_DEBUG
113 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Exit called without init\n"));
114 #endif
115
116 DebugDev_8257DMAInitted = 0;
117 }
118
i8257Config(UINT8 (* cpuread)(UINT16),void (* cpuwrite)(UINT16,UINT8),INT32 (* idle)(INT32),ior_in_functs * read_f,ior_out_functs * write_f)119 void i8257Config(UINT8 (*cpuread)(UINT16), void (*cpuwrite)(UINT16,UINT8), INT32 (*idle)(INT32), ior_in_functs *read_f, ior_out_functs *write_f)
120 {
121 #if defined FBNEO_DEBUG
122 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Config called without init\n"));
123 #endif
124
125 m_in_memr_func = cpuread;
126 m_out_memw_func = cpuwrite;
127
128 for (INT32 i = 0; i < I8257_NUM_CHANNELS; i++) {
129 if (read_f != NULL) m_in_ior_func[i] = (read_f[i] != NULL) ? read_f[i] : null_in;
130 if (write_f != NULL) m_out_iow_func[i] = (write_f[i] != NULL) ? write_f[i] : null_out;
131 }
132
133 if (idle != NULL) m_idle_func = idle;
134 }
135
136 //-------------------------------------------------
137 // device_reset - device-specific reset
138 //-------------------------------------------------
139
i8257Reset()140 void i8257Reset()
141 {
142 #if defined FBNEO_DEBUG
143 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Reset called without init\n"));
144 #endif
145
146 trigger_transfer = 0;
147 m_status &= 0xf0;
148 m_mode = 0;
149 i8257_update_status();
150 }
151
i8257_do_operation(INT32 channel)152 static INT32 i8257_do_operation(INT32 channel)
153 {
154 INT32 done = 0;
155 UINT8 data;
156
157 UINT8 mode = m_rwmode[channel];
158
159 if (m_count[channel] == 0x0000)
160 {
161 m_status |= (0x01 << channel);
162
163 m_out_tc_func(ASSERT_LINE);
164 }
165
166 switch (mode)
167 {
168 case 0:
169 m_address[channel]++;
170 m_count[channel]--;
171 done = (m_count[channel] == 0xFFFF);
172 break;
173
174 case 1:
175 data = m_in_memr_func(m_address[channel]);
176 m_out_iow_func[channel](m_address[channel], data);
177 m_address[channel]++;
178 m_count[channel]--;
179 done = (m_count[channel] == 0xFFFF);
180 break;
181
182 case 2:
183 data = m_in_ior_func[channel](m_address[channel]);
184 m_out_memw_func(m_address[channel], data);
185 m_address[channel]++;
186 m_count[channel]--;
187 done = (m_count[channel] == 0xFFFF);
188 break;
189 }
190
191 if (done)
192 {
193 if ((channel==2) && DMA_MODE_AUTOLOAD(m_mode))
194 {
195 /* in case of autoload at the end channel 3 info is */
196 /* copied to channel 2 info */
197 m_registers[4] = m_registers[6];
198 m_registers[5] = m_registers[7];
199 }
200
201 m_out_tc_func(CLEAR_LINE);
202 }
203
204 return done;
205 }
206
i8257_timer(INT32 id,INT32 param)207 static void i8257_timer(INT32 id, INT32 param)
208 {
209 switch (id)
210 {
211 case TIMER_OPERATION:
212 {
213 INT32 i, channel = 0, rr;
214 INT32 done;
215
216 rr = DMA_MODE_ROTPRIO(m_mode) ? m_rr : 0;
217 for (i = 0; i < I8257_NUM_CHANNELS; i++)
218 {
219 channel = (i + rr) % I8257_NUM_CHANNELS;
220 if ((m_status & (1 << channel)) == 0)
221 {
222 if (m_mode & m_drq & (1 << channel))
223 {
224 break;
225 }
226 }
227 }
228
229 done = i8257_do_operation(channel);
230 m_rr = (channel + 1) & 0x03;
231
232 if (done)
233 {
234 m_drq &= ~(0x01 << channel);
235 trigger_transfer = 1; // i8257_update_status();
236 if (!(DMA_MODE_AUTOLOAD(m_mode) && channel==2))
237 {
238 if (DMA_MODE_TCSTOP(m_mode))
239 {
240 m_mode &= ~(0x01 << channel);
241 }
242 }
243 }
244 break;
245 }
246
247 case TIMER_MSBFLIP:
248 m_msb ^= 1;
249 break;
250
251 case TIMER_DRQ_SYNC:
252 {
253 INT32 channel = param >> 1;
254 INT32 state = param & 0x01;
255
256 /* normalize state */
257 if (state)
258 {
259 m_drq |= 0x01 << channel;
260 m_address[channel] = m_registers[channel * 2];
261 m_count[channel] = m_registers[channel * 2 + 1] & 0x3FFF;
262 m_rwmode[channel] = m_registers[channel * 2 + 1] >> 14;
263 /* clear channel TC */
264 m_status &= ~(0x01 << channel);
265 }
266 else
267 m_drq &= ~(0x01 << channel);
268
269 trigger_transfer = 1; // i8257_update_status();
270 break;
271 }
272 }
273 }
274
i8257_update_status()275 void i8257_update_status()
276 {
277 #if defined FBNEO_DEBUG
278 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257_update_status called without init\n"));
279 #endif
280
281 UINT16 pending_transfer;
282
283 /* no transfer is active right now; is there a transfer pending right now? */
284 pending_transfer = m_drq & (m_mode & 0x0F);
285
286 while (pending_transfer)
287 {
288 m_idle_func(4);
289 i8257_timer(TIMER_OPERATION, 0);
290
291 pending_transfer = m_drq & (m_mode & 0x0F);
292 }
293
294 /* set the halt line */
295 m_out_hrq_func(pending_transfer ? ASSERT_LINE : CLEAR_LINE);
296 }
297
i8257_prepare_msb_flip()298 static void i8257_prepare_msb_flip()
299 {
300 i8257_timer(TIMER_MSBFLIP, 0);
301 }
302
i8257Read(UINT8 offset)303 UINT8 i8257Read(UINT8 offset)
304 {
305 #if defined FBNEO_DEBUG
306 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Read called without init\n"));
307 #endif
308
309 UINT8 data = 0xFF;
310
311 switch (offset & 0x0f)
312 {
313 case 0:
314 case 1:
315 case 2:
316 case 3:
317 case 4:
318 case 5:
319 case 6:
320 case 7:
321 /* DMA address/count register */
322 data = ( m_registers[offset & 7] >> (m_msb ? 8 : 0) ) & 0xFF;
323 i8257_prepare_msb_flip();
324 break;
325
326 case 8:
327 /* DMA status register */
328 data = (UINT8) m_status;
329 /* read resets status ! */
330 m_status &= 0xF0;
331 break;
332
333 default:
334 data = 0xFF;
335 break;
336 }
337
338 return data;
339 }
340
i8257Write(UINT8 offset,UINT8 data)341 void i8257Write(UINT8 offset, UINT8 data)
342 {
343 #if defined FBNEO_DEBUG
344 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Write called without init\n"));
345 #endif
346
347 switch (offset & 0x0f)
348 {
349 case 0:
350 case 1:
351 case 2:
352 case 3:
353 case 4:
354 case 5:
355 case 6:
356 case 7:
357 /* DMA address/count register */
358 if (m_msb)
359 {
360 m_registers[offset & 0x7] |= ((UINT16) data) << 8;
361 }
362 else
363 {
364 m_registers[offset & 0x7] = data;
365 }
366
367 if (DMA_MODE_AUTOLOAD(m_mode))
368 {
369 /* in case of autoload when inserting channel 2 info */
370 /* it is automaticaly copied to channel 3 info */
371 switch(offset)
372 {
373 case 4:
374 case 5:
375 if (m_msb)
376 {
377 m_registers[(offset & 0x7)+2] |= ((UINT16) data) << 8;
378 }
379 else
380 {
381 m_registers[(offset & 0x7)+2] = data;
382 }
383 }
384 }
385
386 i8257_prepare_msb_flip();
387 break;
388
389 case 8:
390 /* DMA mode register */
391 m_mode = data;
392 break;
393 }
394 }
395
i8257_drq_write(INT32 channel,INT32 state)396 void i8257_drq_write(INT32 channel, INT32 state)
397 {
398 #if defined FBNEO_DEBUG
399 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257_drq_write called without init\n"));
400 #endif
401
402 INT32 param = (channel << 1) | (state ? 1 : 0);
403
404 i8257_timer(TIMER_DRQ_SYNC, param);
405 }
406
i8257_do_transfer(INT32)407 void i8257_do_transfer(INT32)
408 {
409 #if defined FBNEO_DEBUG
410 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257_do_transfer called without init\n"));
411 #endif
412
413 if (trigger_transfer)
414 i8257_update_status();
415 trigger_transfer = 0;
416 }
417
i8257Scan()418 void i8257Scan()
419 {
420 #if defined FBNEO_DEBUG
421 if (!DebugDev_8257DMAInitted) bprintf(PRINT_ERROR, _T("i8257Scan called without init\n"));
422 #endif
423
424 SCAN_VAR(m_registers);
425 SCAN_VAR(m_address);
426 SCAN_VAR(m_count);
427 SCAN_VAR(m_rwmode);
428
429 SCAN_VAR(m_mode);
430 SCAN_VAR(m_rr);
431 SCAN_VAR(m_msb);
432 SCAN_VAR(m_drq);
433 SCAN_VAR(m_status);
434
435 SCAN_VAR(trigger_transfer);
436 }
437