1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /***************************************************************************
4
5 Amiga floppy disk controller emulation
6
7 ***************************************************************************/
8
9
10 #include "emu.h"
11 #include "formats/ami_dsk.h"
12 #include "amigafdc.h"
13
14 DEFINE_DEVICE_TYPE(AMIGA_FDC, amiga_fdc_device, "amiga_fdc", "Amiga FDC")
15
FLOPPY_FORMATS_MEMBER(amiga_fdc_device::floppy_formats)16 FLOPPY_FORMATS_MEMBER( amiga_fdc_device::floppy_formats )
17 FLOPPY_ADF_FORMAT
18 FLOPPY_FORMATS_END
19
20 amiga_fdc_device::amiga_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
21 device_t(mconfig, AMIGA_FDC, tag, owner, clock),
22 m_write_index(*this),
23 m_read_dma(*this),
24 m_write_dma(*this),
25 m_write_dskblk(*this),
26 m_write_dsksyn(*this),
27 m_leds(*this, "led%u", 1U),
28 m_fdc_led(*this, "fdc_led"),
29 floppy(nullptr), t_gen(nullptr), dsklen(0), pre_dsklen(0), dsksync(0), dskbyt(0), adkcon(0), dmacon(0), dskpt(0), dma_value(0), dma_state(0)
30 {
31 }
32
device_start()33 void amiga_fdc_device::device_start()
34 {
35 m_write_index.resolve_safe();
36 m_read_dma.resolve_safe(0);
37 m_write_dma.resolve_safe();
38 m_write_dskblk.resolve_safe();
39 m_write_dsksyn.resolve_safe();
40 m_leds.resolve();
41 m_fdc_led.resolve();
42
43 static char const *const names[] = { "0", "1", "2", "3" };
44 for(int i=0; i != 4; i++) {
45 floppy_connector *con = subdevice<floppy_connector>(names[i]);
46 if(con)
47 floppy_devices[i] = con->get_device();
48 else
49 floppy_devices[i] = nullptr;
50 }
51
52 floppy = nullptr;
53
54 t_gen = timer_alloc(0);
55 }
56
57
device_reset()58 void amiga_fdc_device::device_reset()
59 {
60 floppy = nullptr;
61 dsklen = 0x4000;
62 dsksync = 0x4489;
63 adkcon = 0;
64 dmacon = 0;
65 dskpt = 0;
66 dskbyt = 0;
67 pre_dsklen = 0x4000;
68 dma_value = 0;
69 dma_state = DMA_IDLE;
70
71 live_abort();
72 }
73
dma_done()74 void amiga_fdc_device::dma_done()
75 {
76 if(dskbyt & 0x2000) {
77 dskbyt &= ~0x2000;
78 cur_live.pll.stop_writing(floppy, cur_live.tm);
79 }
80
81 dma_state = DMA_IDLE;
82 m_write_dskblk(1);
83 }
84
dma_write(uint16_t value)85 void amiga_fdc_device::dma_write(uint16_t value)
86 {
87 m_write_dma(dskpt, value, 0xffff);
88
89 dskpt += 2;
90 dsklen--;
91
92 if(dsklen & 0x3fff)
93 dma_state = DMA_RUNNING_BYTE_0;
94 else
95 dma_done();
96 }
97
dma_read()98 uint16_t amiga_fdc_device::dma_read()
99 {
100 uint16_t res = m_read_dma(dskpt, 0xffff);
101
102 dskpt += 2;
103 dsklen--;
104
105 // This loses the last word. So does the real hardware.
106 if(dsklen & 0x3fff)
107 dma_state = DMA_RUNNING_BYTE_0;
108 else
109 dma_done();
110
111 return res;
112 }
113
live_start()114 void amiga_fdc_device::live_start()
115 {
116 cur_live.tm = machine().time();
117 cur_live.state = RUNNING;
118 cur_live.next_state = -1;
119 cur_live.shift_reg = 0;
120 cur_live.bit_counter = 0;
121 cur_live.pll.reset(cur_live.tm);
122 cur_live.pll.set_clock(clocks_to_attotime(1));
123 checkpoint_live = cur_live;
124
125 live_run();
126 }
127
checkpoint()128 void amiga_fdc_device::checkpoint()
129 {
130 cur_live.pll.commit(floppy, cur_live.tm);
131 checkpoint_live = cur_live;
132 }
133
rollback()134 void amiga_fdc_device::rollback()
135 {
136 cur_live = checkpoint_live;
137 }
138
live_delay(int state)139 void amiga_fdc_device::live_delay(int state)
140 {
141 cur_live.next_state = state;
142 if(cur_live.tm != machine().time())
143 t_gen->adjust(cur_live.tm - machine().time());
144 }
145
live_sync()146 void amiga_fdc_device::live_sync()
147 {
148 if(!cur_live.tm.is_never()) {
149 if(cur_live.tm > machine().time()) {
150 rollback();
151 live_run(machine().time());
152 cur_live.pll.commit(floppy, cur_live.tm);
153
154 } else {
155 cur_live.pll.commit(floppy, cur_live.tm);
156
157 if(cur_live.next_state != -1) {
158 cur_live.state = cur_live.next_state;
159 cur_live.next_state = -1;
160 }
161 if(cur_live.state == IDLE) {
162 cur_live.pll.stop_writing(floppy, cur_live.tm);
163 cur_live.tm = attotime::never;
164 }
165 }
166 cur_live.next_state = -1;
167 checkpoint();
168 }
169 }
170
live_abort()171 void amiga_fdc_device::live_abort()
172 {
173 if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
174 rollback();
175 live_run(machine().time());
176 }
177
178 cur_live.pll.stop_writing(floppy, cur_live.tm);
179 cur_live.tm = attotime::never;
180 cur_live.state = IDLE;
181 cur_live.next_state = -1;
182 }
183
live_run(const attotime & limit)184 void amiga_fdc_device::live_run(const attotime &limit)
185 {
186 if(cur_live.state == IDLE || cur_live.next_state != -1)
187 return;
188
189 for(;;) {
190 switch(cur_live.state) {
191 case RUNNING: {
192 if(!(dskbyt & 0x2000)) {
193 int bit = cur_live.pll.get_next_bit(cur_live.tm, floppy, limit);
194 if(bit < 0)
195 return;
196
197 cur_live.shift_reg = (cur_live.shift_reg << 1) | bit;
198 cur_live.bit_counter++;
199
200 if((adkcon & 0x0200) && !(cur_live.shift_reg & 0x80)) {
201 cur_live.bit_counter--;
202
203 // Avoid any risk of livelock
204 live_delay(RUNNING_SYNCPOINT);
205 return;
206 }
207
208 if(cur_live.bit_counter > 8)
209 fatalerror("amiga_fdc_device::live_run - cur_live.bit_counter > 8\n");
210
211 if(cur_live.bit_counter == 8) {
212 live_delay(RUNNING_SYNCPOINT);
213 return;
214 }
215 if(dskbyt & 0x1000) {
216 if(cur_live.shift_reg != dsksync) {
217 live_delay(RUNNING_SYNCPOINT);
218 return;
219 }
220 } else {
221 if(cur_live.shift_reg == dsksync) {
222 live_delay(RUNNING_SYNCPOINT);
223 return;
224 }
225 }
226 } else {
227 int bit = (dma_state == DMA_RUNNING_BYTE_0 ? 15 : 7) - cur_live.bit_counter;
228 if(cur_live.pll.write_next_bit((dma_value >> bit) & 1, cur_live.tm, floppy, limit))
229 return;
230 cur_live.bit_counter++;
231 if(cur_live.bit_counter > 8)
232 fatalerror("amiga_fdc_device::live_run - cur_live.bit_counter > 8\n");
233
234 if(cur_live.bit_counter == 8) {
235 live_delay(RUNNING_SYNCPOINT);
236 return;
237 }
238 }
239 break;
240 }
241
242 case RUNNING_SYNCPOINT: {
243 if(!(dskbyt & 0x2000)) {
244 if(cur_live.shift_reg == dsksync) {
245 if(adkcon & 0x0400) {
246 if(dma_state == DMA_WAIT_START) {
247 cur_live.bit_counter = 0;
248
249 if(!(dsklen & 0x3fff))
250 dma_done();
251 else if(dsklen & 0x4000) {
252 dskbyt |= 0x2000;
253 cur_live.bit_counter = 0;
254 dma_value = dma_read();
255
256 } else
257 dma_write(dsksync);
258
259 } else if(dma_state != DMA_IDLE) {
260 dma_write(dsksync);
261 cur_live.bit_counter = 0;
262
263 } else if(cur_live.bit_counter != 8)
264 cur_live.bit_counter = 0;
265 }
266 dskbyt |= 0x1000;
267 m_write_dsksyn(1);
268 } else
269 dskbyt &= ~0x1000;
270
271 if(cur_live.bit_counter == 8) {
272 dskbyt = (dskbyt & 0xff00) | 0x8000 | (cur_live.shift_reg & 0xff);
273 cur_live.bit_counter = 0;
274
275 switch(dma_state) {
276 case DMA_IDLE:
277 case DMA_WAIT_START:
278 break;
279
280 case DMA_RUNNING_BYTE_0:
281 dma_value = (cur_live.shift_reg & 0xff) << 8;
282 dma_state = DMA_RUNNING_BYTE_1;
283 break;
284
285 case DMA_RUNNING_BYTE_1: {
286 dma_value |= cur_live.shift_reg & 0xff;
287 dma_write(dma_value);
288 break;
289 }
290 }
291 }
292 } else {
293 if(cur_live.bit_counter != 8)
294 fatalerror("amiga_fdc_device::live_run - cur_live.bit_counter != 8\n");
295 cur_live.bit_counter = 0;
296
297 switch(dma_state) {
298 case DMA_IDLE:
299 case DMA_WAIT_START:
300 break;
301
302 case DMA_RUNNING_BYTE_0:
303 dma_state = DMA_RUNNING_BYTE_1;
304 break;
305
306 case DMA_RUNNING_BYTE_1: {
307 dma_value = dma_read();
308 break;
309 }
310 }
311 }
312
313 cur_live.state = RUNNING;
314 checkpoint();
315 break;
316 }
317 }
318 }
319 }
320
dma_enabled()321 bool amiga_fdc_device::dma_enabled()
322 {
323 return (dsklen & 0x8000) && ((dmacon & 0x0210) == 0x0210);
324 }
325
dma_check()326 void amiga_fdc_device::dma_check()
327 {
328 bool was_writing = dskbyt & 0x2000;
329 dskbyt &= 0x9fff;
330 if(dma_enabled()) {
331 if(dma_state == IDLE) {
332 dma_state = adkcon & 0x0400 ? DMA_WAIT_START : DMA_RUNNING_BYTE_0;
333 if(dma_state == DMA_RUNNING_BYTE_0) {
334 if(!(dsklen & 0x3fff))
335 dma_done();
336 else if(dsklen & 0x4000) {
337 dskbyt |= 0x2000;
338 cur_live.bit_counter = 0;
339 dma_value = dma_read();
340 }
341 }
342 } else {
343 dskbyt |= 0x4000;
344 if(dsklen & 0x4000)
345 dskbyt |= 0x2000;
346 }
347 } else
348 dma_state = IDLE;
349
350 if(was_writing && !(dskbyt & 0x2000))
351 cur_live.pll.stop_writing(floppy, cur_live.tm);
352 if(!was_writing && (dskbyt & 0x2000))
353 cur_live.pll.start_writing(cur_live.tm);
354
355 }
356
adkcon_set(uint16_t data)357 void amiga_fdc_device::adkcon_set(uint16_t data)
358 {
359 live_sync();
360 adkcon = data;
361 live_run();
362 }
363
adkcon_r(void)364 uint16_t amiga_fdc_device::adkcon_r(void)
365 {
366 return adkcon;
367 }
368
dsklen_w(uint16_t data)369 void amiga_fdc_device::dsklen_w(uint16_t data)
370 {
371 live_sync();
372 if(!(data & 0x8000) || (data == pre_dsklen)) {
373 dsklen = pre_dsklen = data;
374 dma_check();
375
376 } else
377 pre_dsklen = data;
378 live_run();
379 }
380
dskpth_w(uint16_t data)381 void amiga_fdc_device::dskpth_w(uint16_t data)
382 {
383 live_sync();
384 dskpt = (dskpt & 0xffff) | (data << 16);
385 live_run();
386 }
387
dskptl_w(uint16_t data)388 void amiga_fdc_device::dskptl_w(uint16_t data)
389 {
390 live_sync();
391 dskpt = (dskpt & 0xffff0000) | data;
392 live_run();
393 }
394
dskpth_r()395 uint16_t amiga_fdc_device::dskpth_r()
396 {
397 return dskpt >> 16;
398 }
399
dskptl_r()400 uint16_t amiga_fdc_device::dskptl_r()
401 {
402 return dskpt;
403 }
404
dsksync_w(uint16_t data)405 void amiga_fdc_device::dsksync_w(uint16_t data)
406 {
407 live_sync();
408 dsksync = data;
409 live_run();
410 }
411
dmacon_set(uint16_t data)412 void amiga_fdc_device::dmacon_set(uint16_t data)
413 {
414 live_sync();
415 dmacon = data;
416 dma_check();
417 live_run();
418 }
419
dskbytr_r()420 uint16_t amiga_fdc_device::dskbytr_r()
421 {
422 uint16_t res = dskbyt;
423 dskbyt &= 0x7fff;
424 return res;
425 }
426
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)427 void amiga_fdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
428 {
429 live_sync();
430 live_run();
431 }
432
setup_leds()433 void amiga_fdc_device::setup_leds()
434 {
435 if(floppy) {
436 int drive =
437 floppy == floppy_devices[0] ? 0 :
438 floppy == floppy_devices[1] ? 1 :
439 floppy == floppy_devices[2] ? 2 :
440 3;
441
442 m_leds[0] = drive == 0 ? 1 : 0; // update internal drive led
443 m_leds[1] = drive == 1 ? 1 : 0; // update external drive led
444 }
445 }
446
ciaaprb_w(uint8_t data)447 void amiga_fdc_device::ciaaprb_w(uint8_t data)
448 {
449 floppy_image_device *old_floppy = floppy;
450
451 live_sync();
452
453 if(!(data & 0x08))
454 floppy = floppy_devices[0];
455 else if(!(data & 0x10))
456 floppy = floppy_devices[1];
457 else if(!(data & 0x20))
458 floppy = floppy_devices[2];
459 else if(!(data & 0x40))
460 floppy = floppy_devices[3];
461 else
462 floppy = nullptr;
463
464 if(old_floppy != floppy) {
465 if(old_floppy)
466 old_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
467 if(floppy)
468 floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&amiga_fdc_device::index_callback, this));
469 }
470
471 if(floppy) {
472 floppy->ss_w(!(BIT(data, 2)));
473 floppy->dir_w(BIT(data, 1));
474 floppy->stp_w(BIT(data, 0));
475 floppy->mon_w(BIT(data, 7));
476 m_fdc_led = BIT(data, 7); // LED directly connected to FDC motor
477 }
478
479 if(floppy) {
480 if(cur_live.state == IDLE)
481 live_start();
482 } else
483 live_abort();
484
485 setup_leds();
486 live_run();
487 }
488
ciaapra_r()489 uint8_t amiga_fdc_device::ciaapra_r()
490 {
491 uint8_t ret = 0x3c;
492 if(floppy) {
493 //if(!floppy->ready_r()) fixit: seems to not work well with multiple disk drives
494 ret &= ~0x20;
495 if(!floppy->trk00_r())
496 ret &= ~0x10;
497 if(floppy->wpt_r())
498 ret &= ~0x08;
499 if(!floppy->dskchg_r())
500 ret &= ~0x04;
501 }
502
503 return ret;
504 }
505
index_callback(floppy_image_device * floppy,int state)506 void amiga_fdc_device::index_callback(floppy_image_device *floppy, int state)
507 {
508 /* Issue a index pulse when a disk revolution completes */
509 m_write_index(!state);
510 }
511
set_clock(const attotime & period)512 void amiga_fdc_device::pll_t::set_clock(const attotime &period)
513 {
514 for(int i=0; i<38; i++)
515 delays[i] = period*(i+1);
516 }
517
reset(const attotime & when)518 void amiga_fdc_device::pll_t::reset(const attotime &when)
519 {
520 counter = 0;
521 increment = 146;
522 transition_time = 0xffff;
523 history = 0x80;
524 slot = 0;
525 ctime = when;
526 phase_add = 0x00;
527 phase_sub = 0x00;
528 freq_add = 0x00;
529 freq_sub = 0x00;
530 }
531
get_next_bit(attotime & tm,floppy_image_device * floppy,const attotime & limit)532 int amiga_fdc_device::pll_t::get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit)
533 {
534 attotime when = floppy ? floppy->get_next_transition(ctime) : attotime::never;
535
536 for(;;) {
537 attotime etime = ctime+delays[slot];
538 if(etime > limit)
539 return -1;
540 if(transition_time == 0xffff && !when.is_never() && etime >= when)
541 transition_time = counter;
542 if(slot < 8) {
543 uint8_t mask = 1 << slot;
544 if(phase_add & mask)
545 counter += 258;
546 else if(phase_sub & mask)
547 counter += 34;
548 else
549 counter += increment;
550
551 if((freq_add & mask) && increment < 159)
552 increment++;
553 else if((freq_sub & mask) && increment > 134)
554 increment--;
555 } else
556 counter += increment;
557
558 slot++;
559 tm = etime;
560 if(counter & 0x800)
561 break;
562 }
563
564 int bit = transition_time != 0xffff;
565
566 if(transition_time != 0xffff) {
567 static uint8_t const pha[8] = { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 };
568 static uint8_t const phs[8] = { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf };
569 static uint8_t const freqa[4][8] = {
570 { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 },
571 { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 },
572 { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 },
573 { 0, 0, 0, 0, 0, 0, 0, 0 }
574 };
575 static uint8_t const freqs[4][8] = {
576 { 0, 0, 0, 0, 0, 0, 0, 0 },
577 { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 },
578 { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 },
579 { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf },
580 };
581
582 int cslot = transition_time >> 8;
583 phase_add = pha[cslot];
584 phase_sub = phs[cslot];
585 int way = transition_time & 0x400 ? 1 : 0;
586 if(history & 0x80)
587 history = way ? 0x80 : 0x83;
588 else if(history & 0x40)
589 history = way ? history & 2 : (history & 2) | 1;
590 freq_add = freqa[history & 3][cslot];
591 freq_sub = freqs[history & 3][cslot];
592 history = way ? (history >> 1) | 2 : history >> 1;
593
594 } else
595 phase_add = phase_sub = freq_add = freq_sub = 0;
596
597 counter &= 0x7ff;
598
599 ctime = tm;
600 transition_time = 0xffff;
601 slot = 0;
602
603 return bit;
604 }
605
start_writing(const attotime & tm)606 void amiga_fdc_device::pll_t::start_writing(const attotime & tm)
607 {
608 write_start_time = tm;
609 write_position = 0;
610 }
611
stop_writing(floppy_image_device * floppy,const attotime & tm)612 void amiga_fdc_device::pll_t::stop_writing(floppy_image_device *floppy, const attotime &tm)
613 {
614 commit(floppy, tm);
615 write_start_time = attotime::never;
616 }
617
write_next_bit(bool bit,attotime & tm,floppy_image_device * floppy,const attotime & limit)618 bool amiga_fdc_device::pll_t::write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit)
619 {
620 if(write_start_time.is_never()) {
621 write_start_time = ctime;
622 write_position = 0;
623 }
624
625 for(;;) {
626 attotime etime = ctime+delays[slot];
627 if(etime > limit)
628 return true;
629 uint16_t pre_counter = counter;
630 counter += increment;
631 if(bit && !(pre_counter & 0x400) && (counter & 0x400))
632 if(write_position < ARRAY_LENGTH(write_buffer))
633 write_buffer[write_position++] = etime;
634 slot++;
635 tm = etime;
636 if(counter & 0x800)
637 break;
638 }
639
640 counter &= 0x7ff;
641
642 ctime = tm;
643 slot = 0;
644
645 return false;
646 }
647
648
commit(floppy_image_device * floppy,const attotime & tm)649 void amiga_fdc_device::pll_t::commit(floppy_image_device *floppy, const attotime &tm)
650 {
651 if(write_start_time.is_never() || tm == write_start_time)
652 return;
653
654 if(floppy)
655 floppy->write_flux(write_start_time, tm, write_position, write_buffer);
656 write_start_time = tm;
657 write_position = 0;
658 }
659