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 FBA_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 FBA_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 FBA_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 FBA_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 FBA_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 FBA_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 FBA_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 FBA_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 FBA_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