1 /*
2  * mfp.cpp - MFP emulation
3  *
4  * Copyright (c) 2001-2005 Petr Stehlik of ARAnyM dev team (see AUTHORS)
5  *
6  * Copied almost bit-by-bit from STonC's mfp.c (thanks, Laurent!)
7  *
8  * This file is part of the ARAnyM project which builds a new and powerful
9  * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
10  *
11  * ARAnyM is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * ARAnyM is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with ARAnyM; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25 
26 #include "sysdeps.h"
27 #include "hardware.h"
28 #include "cpu_emulation.h"
29 #include "memory-uae.h"
30 #include "mfp.h"
31 
32 #define DEBUG 0
33 #include "debug.h"
34 
MFP_Timer(int value)35 MFP_Timer::MFP_Timer(int value)
36 {
37 	name = 'A' + value;
38 	reset();
39 }
40 
reset()41 void MFP_Timer::reset()
42 {
43 	control = start_data = current_data = 0;
44 	state = false;
45 }
46 
isRunning()47 bool MFP_Timer::isRunning()
48 {
49 	return ((control & 0x0f) > 0);
50 }
51 
setControl(uint8 value)52 void MFP_Timer::setControl(uint8 value)
53 {
54 	control = value & 0x0f;
55 	if (value & 0x10)
56 		state = false;
57 	// D(bug("Set MFP Timer%c control to $%x", name, value));
58 }
59 
getControl()60 uint8 MFP_Timer::getControl()
61 {
62 	return control | (state << 5);
63 }
64 
setData(uint8 value)65 void MFP_Timer::setData(uint8 value)
66 {
67 	// D(bug("Set MFP Timer%c data to %d", name, value));
68 	start_data = value;
69 	if (! isRunning())
70 		current_data = value;
71 }
72 
resetCounter()73 void MFP_Timer::resetCounter()
74 {
75 	// D(bug("reset of Timer%c", name));
76 	if (isRunning()) {
77 		state = true;
78 		current_data = start_data;
79 	}
80 }
81 
getData()82 uint8 MFP_Timer::getData()
83 {
84 	// D(bug("get MFP Timer%c data = %d", name, current_data));
85 
86 	if (isRunning() && current_data > 2)
87 		current_data--;		// hack to overcome microseconds delays in TOS (e.g. at $E02570)
88 
89 	return current_data;
90 }
91 
compute_timer_freq()92 int MFP_Timer::compute_timer_freq()
93 {
94 #define MFP_FREQ	2457600UL
95 	int freq;
96 	switch(control) {
97 		case 1: freq = MFP_FREQ /  4; break;
98 		case 2: freq = MFP_FREQ / 10; break;
99 		case 3: freq = MFP_FREQ / 16; break;
100 		case 4: freq = MFP_FREQ / 50; break;
101 		case 5: freq = MFP_FREQ / 64; break;
102 		case 6: freq = MFP_FREQ /100; break;
103 		case 7: freq = MFP_FREQ /200; break;
104 		default: freq = MFP_FREQ / 64; break; // TOS default
105 	}
106 	if (start_data)
107 		freq = freq / start_data;
108 	else
109 		freq = freq / 192; // TOS default
110 
111 	return freq;
112 }
113 
114 /*************************************************************************/
115 
MFP(memptr addr,uint32 size)116 MFP::MFP(memptr addr, uint32 size) : BASE_IO(addr, size)
117 {
118 	reset();
119 }
120 
121 #define MFP_VR_VECTOR 0xF0
122 #define MFP_VR_SEI 0x08         /* Software end of interrupt,
123                                 need software cancelling in inservice ? */
124 #define MFP_VR_AEI 0x00         /* Automatic end of interrupt,
125                                 ??? */
reset()126 void MFP::reset()
127 {
128 	input = 0xff;
129 	GPIP_data = input;
130 	vr = 0x0100;
131 	active_edge = 0;
132 	data_direction = 0;
133 	irq_enable = 0;
134 	irq_pending = 0;
135 	irq_inservice = 0;
136 	irq_mask = 0;
137 	timerCounter = 0;
138 
139 	A.reset();
140 	B.reset();
141 	C.reset();
142 	D.reset();
143 }
144 
handleRead(memptr addr)145 uint8 MFP::handleRead(memptr addr)
146 {
147 	addr -= getHWoffset();
148 	if (addr > getHWsize())
149 		return 0;	// unhandled
150 
151 	uint8 value;
152 	switch(addr) {
153 		case 0x01:	value = (GPIP_data & ~ 0x21) | getYAMAHA()->parallel->getBusy();
154 					break;
155 
156 		case 0x03:	value = active_edge;
157 					break;
158 
159 		case 0x05:	value = data_direction;
160 					break;
161 
162 		case 0x07:	value = irq_enable >> 8;
163 					break;
164 
165 		case 0x09:	value = irq_enable;
166 					break;
167 
168 		case 0x0b:	value = 0x20; //(irq_pending >> 8) | (tA->getControl() & 0x10);	// finish
169 					break;
170 
171 		case 0x0d:	// D(bug("Read: TimerC IRQ %s pending", (irq_pending & 0x20) ? "" : "NOT"));
172 					value = irq_pending;
173 					break;
174 
175 		case 0xf:	value = irq_inservice >> 8;
176 					break;
177 
178 		case 0x11:	// D(bug("Read: TimerC IRQ %s in-service", (irq_inservice & 0x20) ? "" : "NOT"));
179 					value = irq_inservice;
180 					break;
181 
182 		case 0x13:	value = irq_mask >> 8;
183 					break;
184 
185 		case 0x15:	// D(bug("Read: TimerC IRQ %s masked", (irq_mask & 0x20) ? "" : "NOT"));
186 					value = irq_mask;
187 					break;
188 
189 		case 0x17:	value = vr;
190 					break;
191 
192 		case 0x19:	value = A.getControl();
193 					break;
194 
195 		case 0x1b:	value = B.getControl();
196 					break;
197 
198 		case 0x1d:	value = (C.getControl() << 4) | D.getControl();
199 					break;
200 
201 		case 0x1f:	value = A.getData();
202 					break;
203 
204 		case 0x21:	value = B.getData();
205 					break;
206 
207 		case 0x23:	value = C.getData();
208 					break;
209 
210 		case 0x25:	value = D.getData();
211 					break;
212 
213 		case 0x2d:	value = 0x80;	// for Linux/m68k
214 					break;
215 
216 		default: value = 0;
217 	};
218 	D(bug("Reading MFP data from %04lx = %d ($%02x) at %06x", addr, value, value, showPC()));
219 	return value;
220 }
221 
set_active_edge(uint8 value)222 void MFP::set_active_edge(uint8 value)
223 {
224 static int map_gpip_to_ier[8] = {0, 1, 2, 3, 6, 7, 14, 15};
225   /* AER : 1=Rising (0->1), 0=Falling (1->0) */
226 
227   /* [mfp.txt]
228    * The edge bit is simply one input to an exclusive-or
229    * gate, with the other input coming from the input buffer and the output going
230    * to a 1-0 transition detector. Thus, depending upon the state of the input,
231    * writing the AER can cause an interrupt-producing transition
232    */
233   /* this means : a = input xor aer, and interrupt when a 1->0.
234    *  ____before____ ______after_____ ___result___
235    *  input  aer  a   input  val  a    interrupt
236    *    0     0   0     0     1   1
237    *    1     0   1     1     1   0       yes
238    *    0     1   1     0     0   0       yes
239    *    1     1   0     1     0   1
240    */
241 	int i, j;
242 	for(j = 0, i = 1; j < 8; j++, i <<= 1) {
243 		if( ((active_edge & i) != (value & i))        /* if AER changes */
244 				&& (! (data_direction & i))               /* for input lines */
245 				&& ((active_edge & i) != (input & i))) {
246 			IRQ(map_gpip_to_ier[j], 1);
247 		}
248 	}
249 
250 	active_edge = value;
251 }
252 
handleWrite(memptr addr,uint8 value)253 void MFP::handleWrite(memptr addr, uint8 value) {
254 	addr -= getHWoffset();
255 	if (addr > getHWsize())
256 		return;	// unhandled
257 
258 	D(bug("Writing MFP data to %04lx = %d ($%02x) at %06x", addr, value, value, showPC()));
259 	switch(addr) {
260 		case 0x01:	//GPIP_data = value;
261 					GPIP_data &= ~data_direction;
262 					GPIP_data |= value & data_direction;
263 					D(bug("New GPIP=$%x from PC=$%x", GPIP_data, showPC()));
264 					break;
265 
266 		case 0x03:	set_active_edge(value);
267 					break;
268 
269 		case 0x05:	data_direction = value;
270 					GPIP_data &= data_direction;
271 					GPIP_data |= input & (~data_direction);
272 					D(bug("GPIP Data Direction set to %02x", value));
273 					break;
274 
275 		case 0x07:	irq_enable = (irq_enable & 0x00ff) | (value << 8);
276 					// cancel any pending interrupts, but do not alter ISR
277 					irq_pending &= irq_enable;
278 					// mfp_check_timers_ier();
279 					break;
280 
281 		case 0x09:
282 #ifdef DEBUG_IER
283 					if ((irq_enable ^ value) & 0x20) {
284 						D(bug("Write: TimerC IRQ %sabled", (value & 20) ? "en" : "dis"));
285 					}
286 					if ((irq_enable ^ value) & 0x40) {
287 						D(bug("Write: IKBD IRQ %sabled", (value & 40) ? "en" : "dis"));
288 					}
289 #endif /* DEBUG */
290 					irq_enable = (irq_enable & 0xff00) | value;
291 					// cancel any pending interrupts, but do not alter ISR
292 					irq_pending &= irq_enable;
293 					// mfp_check_timers_ier();
294 					break;
295 
296   /* [mfp.txt]
297    * IPRA and IPRB are also writeable and a pending interrupt can be cleared
298    * without going through the acknowledge sequence by writing a zero to the
299    * appropriate bit. This allows any one bit to be cleared, without altering any
300    * other bits, simply by writing all ones except for the bit position to be
301    * cleared on IPRA or IPRB.
302    */
303 
304 		case 0x0b:	irq_pending &= (value << 8) | 0xff;
305   /* if no unmasked interrupt pending any more, there's no point in
306    * keeping F_MFP active
307    */
308 				if ((irq_pending & irq_mask) == 0) {
309 					TriggerMFP(false);
310 				}
311 				break;
312 
313 		case 0x0d:
314 #ifdef DEBUG_IPR
315 					if ((irq_pending ^ value) & 0x20) {
316 						D(bug("Write: TimerC IRQ %s pending", (value & 20) ? "" : "NOT"));
317 					}
318 #endif /* DEBUG */
319 					irq_pending &= 0xff00 | value;
320 				if ((irq_pending & irq_mask) == 0) {
321 					TriggerMFP(false);
322 				}
323 
324 					break;
325 
326   /* [mfp.txt]
327    * Only a zero may be written into any bit of ISRA and ISRB; thus the
328    * in-service may be cleared in software but cannot be set in software.
329    */
330 		case 0xf:	irq_inservice &= (value << 8) | 0xff;
331 					if (irq_pending & irq_mask) {
332 						// unmasked interrupt pending, signal it to cpu
333 						TriggerMFP(true);
334 					}
335 					break;
336 
337 		case 0x11:
338 #ifdef DEBUG_ISR
339 					if ((irq_inservice ^ value) & 0x20) {
340 						D(bug("Write: TimerC IRQ %s in-service at %08x", (value & 20) ? "" : "NOT", showPC()));
341 					}
342 #endif /* DEBUG */
343 					irq_inservice &= 0xff00 | value;
344 					if (irq_pending & irq_mask) {
345 						// unmasked interrupt pending, signal it to cpu
346 						TriggerMFP(true);
347 					}
348 					break;
349 
350 		case 0x13:	irq_mask = (irq_mask & 0x00ff) | (value << 8);
351 					TriggerMFP(irq_pending & irq_mask);
352 					break;
353 
354 		case 0x15:
355 #ifdef DEBUG_IMR
356 					if ((irq_mask ^ value) & 0x20) {
357 						D(bug("Write: TimerC IRQ %s masked", (value & 20) ? "" : "NOT"));
358 					}
359 #endif /* DEBUG */
360 					irq_mask = (irq_mask & 0xff00) | value;
361 					TriggerMFP(irq_pending & irq_mask);
362 					break;
363 
364 		case 0x17:	vr = value;
365 					if (vr & MFP_VR_SEI) {
366 						// software end-of-interrupt mode
367 					}
368 					else {
369 						// automatic end-of-interrupt mode : reset inservice bits?
370 						irq_inservice = 0;
371 						// try to pass a vector
372 						TriggerMFP(irq_pending & irq_mask);
373 					}
374 					break;
375 
376 		case 0x19:	A.setControl(value);
377 					break;
378 
379 		case 0x1b:	B.setControl(value);
380 					break;
381 
382 		case 0x1d:	C.setControl(value >> 4);
383 					D.setControl(value & 0x0f);
384 					break;
385 
386 		case 0x1f:	A.setData(value);
387 					break;
388 
389 		case 0x21:	B.setData(value);
390 					break;
391 
392 		case 0x23:	C.setData(value);
393 					break;
394 
395 		case 0x25:	D.setData(value);
396 					break;
397 
398 		case 0x27:
399 		case 0x29:
400 		case 0x2b:
401 		case 0x2d:
402 		case 0x2f:
403 		default:
404 			break;
405 	};
406 }
407 
408 /*
409  * setGPIPbit sets input port bits (peripheral interrupt request)
410  */
setGPIPbit(int mask,int value)411 void MFP::setGPIPbit(int mask, int value)
412 {
413 	static int map_gpip_to_ier[8] = {0, 1, 2, 3, 6, 7, 14, 15};
414 	mask &= 0xff;
415 
416 	int oldGPIP = GPIP_data;
417 	input &= ~mask;
418 	input |= (value & mask);
419 	/* if output port, no interrupt */
420 	mask &= ~data_direction;
421 
422 	GPIP_data &= ~mask;
423 	GPIP_data |= (value & mask);
424 	GPIP_data &= 0xFF;
425 	D(bug("setGPIPbit($%x, $%x): old=$%x, new=$%x", mask, value, oldGPIP, GPIP_data));
426 	int i, j;
427 	for(j = 0, i = 1; j < 8; j++, i <<= 1) {
428 		if ((oldGPIP & i) != (GPIP_data & i)) {
429 			D(bug("setGPIPbit: i=$%x, irq_enable=$%x, old=$%x, new=$%x", i, irq_enable, oldGPIP, GPIP_data));
430 			if (active_edge & i) {
431 				/* interrupt when going from 0 to 1  */
432 				if (oldGPIP & i)
433 					continue;
434 			}
435 			else {
436 				/* interrupt when going from 1 to 0  */
437 				if (GPIP_data & i)
438 					continue;
439 			}
440 			D(bug("calling IRQ(%d)->%d", j, map_gpip_to_ier[j]));
441 			IRQ(map_gpip_to_ier[j], 1);
442 		}
443 	}
444 }
445 
446 /*
447         Ask for an interrupt. Depending on diverse factors, an
448         interrupt will eventually occur the next time
449         MFP:doInterrupt() is called.
450 
451         0:  IO port 0 : Printer Busy
452         1:  IO port 1 : Printer Acknowledge (RS232 Carrier detect on ST)
453         2:  IO port 2 : MIDI Interrupt (RS232 CTS on ST)
454         3:  IO port 3 : DSP Interrupt (Blitter on ST)
455         4:  timer D
456         5:  timer C
457         6:  IO port 4 : ACIA (IKBD/MIDI)
458         7:  IO port 5 : FDC/SCSI/IDE
459         8:  Timer B   : Display Enable
460         9:  Send error
461         10: Send buffer empty
462         11: Receive error
463         12: Receive buffer full
464         13: Timer A   : DMA Sound Interrupt
465         14: IO port 6 : RS232 ring
466         15: IO port 7 : DMA Sound Interrupt (monochrome detect on ST)
467 */
468 
IRQ(int int_level,int count)469 void MFP::IRQ(int int_level, int count)
470 {
471 	int i = 1 << int_level;
472 
473 	if (int_level == 5) C.resetCounter(); // special hack for TimerC
474 
475         /* interrupt enabled ? */
476         if( (irq_enable & i) == 0) {
477 				// panicbug("interrupt %d not enabled", int_level);
478                 return;
479 		}
480 
481 	if (int_level == 5) timerCounter += count; // special hack for TimerC
482 
483         /* same interrupt already pending */
484         if( (irq_pending & i) ) {
485 				D(bug("same interrupt %d already pending", int_level));
486                 return;
487 		}
488 
489 
490         /* ok, we will request an interrupt to the mfp */
491 #if 0
492         if(int_level) {
493           panicbug( "mfp ask interrupt %d\n", int_level);
494         }
495 #endif
496         irq_pending |= i;
497         /* interrupt masked ? */
498         if( ! (irq_mask & i) ) {
499                 /* irq_pending set but no irq : stop here */
500                D(bug("irq_pending set but no irq"));
501           return;
502         }
503                 /* highest priority ? */
504         if(irq_inservice > i) {
505                 /* no, do nothing (the mfp will check when the current
506                   interrupt resumes, i.e. when irq_inservice is being cleared). */
507 				D(bug("irq_inservice has higher priority"));
508                 return;
509         }
510         /* say we want to interrupt the cpu */
511         TriggerMFP(true);
512         /* when UAE CPU finds this flag set, according to the current IPL,
513           the function MFP::doInterrupt() will be called. This function
514           will then decide what to do, and will treat the request that
515           has the highest priority at that time.
516         */
517 }
518 
519 // return : vector number, or zero if no interrupt
doInterrupt()520 int MFP::doInterrupt()
521 {
522         int j, vector;
523         unsigned i;
524 
525         /* what's happening here ? */
526 #if 0
527         panicbug( "starting mfp_do_interrupt\n");
528         panicbug( "ier %04x, ipr %04x, isr %04x, imr %04x\n",
529                irq_enable, irq_pending, irq_inservice, irq_mask);
530 #endif
531 
532         /* any pending interrupts? */
533         for(j = 15, i = 0x8000; i; j--, i>>=1) {
534                 if(irq_pending & i & irq_mask)
535                         break;
536         }
537         if(i == 0) {
538           /* this shouldn't happen :-) */
539                 panicbug( "mfp_do_interrupt called with no pending interrupt\n");
540                 TriggerMFP(false);
541                 return 0;
542         }
543         if(irq_inservice >= i) {
544                 /* Still busy. We shouldn't come here. */
545 
546 				/*	Running MagiC this happens all the time.
547 					The panicbug log makes the whole console output unusable.
548 					Thus it is commented out.
549 
550 				panicbug( "mfp_do_interrupt called when "
551                         "another higher priority interrupt is running\n");
552 				*/
553 
554                 TriggerMFP(false);
555                 return 0;
556         }
557 
558         /* ok, do the interrupt, i.e. "pass the vector". */
559         vector = (vr & MFP_VR_VECTOR) + j;
560 
561 	if (j != 5 || --timerCounter <= 0) {	// special hack for TimerC
562         irq_pending &= ~i;
563         TriggerMFP(false);
564 	}
565 
566         if(vr & MFP_VR_SEI) {
567                 /* software mode of interrupt : irq_inservice will remain set until
568                    explicitely cleared by writing on it */
569                 irq_inservice |= i;
570         } else {
571                 /* automatic mode of interrupt : irq_inservice automatically cleared
572                    when the interrupt starts (which is now). In this case,
573                    we must keep the flag raised if another unmasked
574                    interrupt remains pending, since irq_inservice will not tell us
575                    to re-raise the flag.
576                 */
577                 if((irq_pending & irq_mask)) {
578                   /* if other unmasked interrupt pending, keep the flag */
579                   TriggerMFP(true);
580                 }
581         }
582 
583 #if 0
584         panicbug( "MFP::doInterrupt : vector %d\n", vector);
585 #endif
586         return vector;
587 }
588 
timerC_ms_ticks()589 int MFP::timerC_ms_ticks()
590 {
591 	int freq = C.compute_timer_freq();
592 
593 	return 1000 / freq;
594 }
595