1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4
5 Commodore 2040 floppy disk controller emulation
6
7 **********************************************************************/
8
9 /*
10
11 TODO:
12
13 - write protect
14 - separate read/write methods
15
16 */
17
18 #include "emu.h"
19 #include "c2040fdc.h"
20
21
22
23 //**************************************************************************
24 // MACROS / CONSTANTS
25 //**************************************************************************
26
27 #define LOG 0
28
29 #define GCR_DECODE(_e, _i) \
30 ((BIT(_e, 6) << 7) | (BIT(_i, 7) << 6) | (_e & 0x33) | (BIT(_e, 2) << 3) | (_i & 0x04))
31
32 #define GCR_ENCODE(_e, _i) \
33 ((_e & 0xc0) << 2 | (_i & 0x80) | (_e & 0x3c) << 1 | (_i & 0x04) | (_e & 0x03))
34
35
36
37 //**************************************************************************
38 // DEVICE DEFINITIONS
39 //**************************************************************************
40
41 DEFINE_DEVICE_TYPE(C2040_FDC, c2040_fdc_device, "c2040_fdc", "Commodore 2040 FDC")
42
43
44 //-------------------------------------------------
45 // ROM( c2040_fdc )
46 //-------------------------------------------------
47
ROM_START(c2040_fdc)48 ROM_START( c2040_fdc )
49 ROM_REGION( 0x800, "gcr", 0)
50 ROM_LOAD( "901467.uk6", 0x000, 0x800, CRC(a23337eb) SHA1(97df576397608455616331f8e837cb3404363fa2) )
51 ROM_END
52
53
54 //-------------------------------------------------
55 // rom_region - device-specific ROM region
56 //-------------------------------------------------
57
58 const tiny_rom_entry *c2040_fdc_device::device_rom_region() const
59 {
60 return ROM_NAME( c2040_fdc );
61 }
62
63
64
65 //**************************************************************************
66 // LIVE DEVICE
67 //**************************************************************************
68
69 //-------------------------------------------------
70 // c2040_fdc_device - constructor
71 //-------------------------------------------------
72
c2040_fdc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)73 c2040_fdc_device::c2040_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
74 device_t(mconfig, C2040_FDC, tag, owner, clock),
75 m_write_sync(*this),
76 m_write_ready(*this),
77 m_write_error(*this),
78 m_gcr_rom(*this, "gcr"),
79 m_floppy0(nullptr),
80 m_floppy1(nullptr),
81 m_mtr0(1),
82 m_mtr1(1),
83 m_stp0(0),
84 m_stp1(0),
85 m_ds(0),
86 m_ds0(0),
87 m_ds1(0),
88 m_drv_sel(0),
89 m_mode_sel(0),
90 m_rw_sel(0), m_odd_hd(0), m_pi(0),
91 t_gen(nullptr)
92 {
93 cur_live.tm = attotime::never;
94 cur_live.state = IDLE;
95 cur_live.next_state = -1;
96 cur_live.write_position = 0;
97 cur_live.write_start_time = attotime::never;
98 cur_live.drv_sel = m_drv_sel;
99 }
100
101
102 //-------------------------------------------------
103 // device_start - device-specific startup
104 //-------------------------------------------------
105
device_start()106 void c2040_fdc_device::device_start()
107 {
108 // resolve callbacks
109 m_write_sync.resolve_safe();
110 m_write_ready.resolve_safe();
111 m_write_error.resolve_safe();
112
113 // allocate timer
114 t_gen = timer_alloc(0);
115
116 // register for state saving
117 save_item(NAME(m_mtr0));
118 save_item(NAME(m_mtr1));
119 save_item(NAME(m_stp0));
120 save_item(NAME(m_stp1));
121 save_item(NAME(m_ds));
122 save_item(NAME(m_ds0));
123 save_item(NAME(m_ds1));
124 save_item(NAME(m_drv_sel));
125 save_item(NAME(m_mode_sel));
126 save_item(NAME(m_rw_sel));
127 }
128
129
130 //-------------------------------------------------
131 // device_clock_changed - called when the
132 // device clock is altered in any way
133 //-------------------------------------------------
134
device_clock_changed()135 void c2040_fdc_device::device_clock_changed()
136 {
137 m_period = attotime::from_hz(clock());
138 }
139
140
141 //-------------------------------------------------
142 // device_reset - device-specific reset
143 //-------------------------------------------------
144
device_reset()145 void c2040_fdc_device::device_reset()
146 {
147 live_abort();
148 }
149
150
151 //-------------------------------------------------
152 // device_timer - handler timer events
153 //-------------------------------------------------
154
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)155 void c2040_fdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
156 {
157 live_sync();
158 live_run();
159 }
160
get_floppy()161 floppy_image_device* c2040_fdc_device::get_floppy()
162 {
163 return cur_live.drv_sel ? m_floppy1 : m_floppy0;
164 }
165
live_start()166 void c2040_fdc_device::live_start()
167 {
168 cur_live.tm = machine().time();
169 cur_live.state = RUNNING;
170 cur_live.next_state = -1;
171
172 cur_live.shift_reg = 0;
173 cur_live.shift_reg_write = 0;
174 cur_live.cycle_counter = 0;
175 cur_live.cell_counter = 0;
176 cur_live.bit_counter = 0;
177 cur_live.ds = m_ds;
178 cur_live.drv_sel = m_drv_sel;
179 cur_live.mode_sel = m_mode_sel;
180 cur_live.rw_sel = m_rw_sel;
181 cur_live.pi = m_pi;
182
183 checkpoint_live = cur_live;
184
185 live_run();
186 }
187
checkpoint()188 void c2040_fdc_device::checkpoint()
189 {
190 get_next_edge(machine().time());
191 checkpoint_live = cur_live;
192 }
193
rollback()194 void c2040_fdc_device::rollback()
195 {
196 cur_live = checkpoint_live;
197 get_next_edge(cur_live.tm);
198 }
199
start_writing(const attotime & tm)200 void c2040_fdc_device::start_writing(const attotime &tm)
201 {
202 cur_live.write_start_time = tm;
203 cur_live.write_position = 0;
204 }
205
stop_writing(const attotime & tm)206 void c2040_fdc_device::stop_writing(const attotime &tm)
207 {
208 commit(tm);
209 cur_live.write_start_time = attotime::never;
210 }
211
write_next_bit(bool bit,const attotime & limit)212 bool c2040_fdc_device::write_next_bit(bool bit, const attotime &limit)
213 {
214 if(cur_live.write_start_time.is_never()) {
215 cur_live.write_start_time = cur_live.tm;
216 cur_live.write_position = 0;
217 }
218
219 attotime etime = cur_live.tm + m_period;
220 if(etime > limit)
221 return true;
222
223 if(bit && cur_live.write_position < ARRAY_LENGTH(cur_live.write_buffer))
224 cur_live.write_buffer[cur_live.write_position++] = cur_live.tm - m_period;
225
226 if (LOG) logerror("%s write bit %u (%u)\n", cur_live.tm.as_string(), cur_live.bit_counter, bit);
227
228 return false;
229 }
230
commit(const attotime & tm)231 void c2040_fdc_device::commit(const attotime &tm)
232 {
233 if(cur_live.write_start_time.is_never() || tm == cur_live.write_start_time || !cur_live.write_position)
234 return;
235
236 if (LOG) logerror("%s committing %u transitions since %s\n", tm.as_string(), cur_live.write_position, cur_live.write_start_time.as_string());
237
238 if(get_floppy())
239 get_floppy()->write_flux(cur_live.write_start_time, tm, cur_live.write_position, cur_live.write_buffer);
240
241 cur_live.write_start_time = tm;
242 cur_live.write_position = 0;
243 }
244
live_delay(int state)245 void c2040_fdc_device::live_delay(int state)
246 {
247 cur_live.next_state = state;
248 if(cur_live.tm != machine().time())
249 t_gen->adjust(cur_live.tm - machine().time());
250 else
251 live_sync();
252 }
253
live_sync()254 void c2040_fdc_device::live_sync()
255 {
256 if(!cur_live.tm.is_never()) {
257 if(cur_live.tm > machine().time()) {
258 rollback();
259 live_run(machine().time());
260 commit(cur_live.tm);
261 } else {
262 commit(cur_live.tm);
263 if(cur_live.next_state != -1) {
264 cur_live.state = cur_live.next_state;
265 cur_live.next_state = -1;
266 }
267 if(cur_live.state == IDLE) {
268 stop_writing(cur_live.tm);
269 cur_live.tm = attotime::never;
270 }
271 }
272 cur_live.next_state = -1;
273 checkpoint();
274 }
275 }
276
live_abort()277 void c2040_fdc_device::live_abort()
278 {
279 if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
280 rollback();
281 live_run(machine().time());
282 }
283
284 stop_writing(cur_live.tm);
285
286 cur_live.tm = attotime::never;
287 cur_live.state = IDLE;
288 cur_live.next_state = -1;
289 cur_live.write_position = 0;
290 cur_live.write_start_time = attotime::never;
291
292 cur_live.ready = 1;
293 cur_live.sync = 1;
294 cur_live.error = 1;
295 }
296
live_run(const attotime & limit)297 void c2040_fdc_device::live_run(const attotime &limit)
298 {
299 if(cur_live.state == IDLE || cur_live.next_state != -1)
300 return;
301
302 for(;;) {
303 switch(cur_live.state) {
304 case RUNNING: {
305 bool syncpoint = false;
306
307 if (cur_live.tm > limit)
308 return;
309
310 int bit = get_next_bit(cur_live.tm, limit);
311 if(bit < 0)
312 return;
313
314 int cell_counter = cur_live.cell_counter;
315
316 if (bit) {
317 cur_live.cycle_counter = cur_live.ds;
318 cur_live.cell_counter = 0;
319 } else {
320 cur_live.cycle_counter++;
321 }
322
323 if (cur_live.cycle_counter == 16) {
324 cur_live.cycle_counter = cur_live.ds;
325
326 cur_live.cell_counter++;
327 cur_live.cell_counter &= 0xf;
328 }
329
330 if (!BIT(cell_counter, 1) && BIT(cur_live.cell_counter, 1)) {
331 // read bit
332 cur_live.shift_reg <<= 1;
333 cur_live.shift_reg |= !(BIT(cur_live.cell_counter, 3) || BIT(cur_live.cell_counter, 2));
334 cur_live.shift_reg &= 0x3ff;
335
336 if (LOG) logerror("%s read bit %u (%u) >> %03x, rw=%u mode=%u\n", cur_live.tm.as_string(), cur_live.bit_counter,
337 !(BIT(cur_live.cell_counter, 3) || BIT(cur_live.cell_counter, 2)), cur_live.shift_reg, cur_live.rw_sel, cur_live.mode_sel);
338
339 // write bit
340 if (!cur_live.rw_sel) { // TODO WPS
341 write_next_bit(BIT(cur_live.shift_reg_write, 9), limit);
342 }
343
344 syncpoint = true;
345 }
346
347 int sync = !((cur_live.shift_reg == 0x3ff) && cur_live.rw_sel);
348
349 if (!sync) {
350 cur_live.bit_counter = 0;
351 } else if (!BIT(cell_counter, 1) && BIT(cur_live.cell_counter, 1) && cur_live.sync) {
352 cur_live.bit_counter++;
353 if (cur_live.bit_counter == 10) {
354 cur_live.bit_counter = 0;
355 }
356 }
357
358 // update GCR
359 if (cur_live.rw_sel) {
360 cur_live.i = (cur_live.rw_sel << 10) | cur_live.shift_reg;
361 } else {
362 cur_live.i = (cur_live.rw_sel << 10) | ((cur_live.pi & 0xf0) << 1) | (cur_live.mode_sel << 4) | (cur_live.pi & 0x0f);
363 }
364
365 cur_live.e = m_gcr_rom->base()[cur_live.i];
366
367 int ready = !(BIT(cell_counter, 1) && !BIT(cur_live.cell_counter, 1) && (cur_live.bit_counter == 9));
368
369 if (!ready) {
370 // load write shift register
371 cur_live.shift_reg_write = GCR_ENCODE(cur_live.e, cur_live.i);
372
373 if (LOG) logerror("%s load write shift register %03x\n",cur_live.tm.as_string(),cur_live.shift_reg_write);
374 } else if (BIT(cell_counter, 1) && !BIT(cur_live.cell_counter, 1)) {
375 // clock write shift register
376 cur_live.shift_reg_write <<= 1;
377 cur_live.shift_reg_write &= 0x3ff;
378
379 if (LOG) logerror("%s write shift << %03x\n",cur_live.tm.as_string(),cur_live.shift_reg_write);
380 }
381
382 int error = !(BIT(cur_live.e, 3) || ready);
383
384 if (ready != cur_live.ready) {
385 if (LOG) logerror("%s READY %u\n", cur_live.tm.as_string(),ready);
386 cur_live.ready = ready;
387 syncpoint = true;
388 }
389
390 if (sync != cur_live.sync) {
391 if (LOG) logerror("%s SYNC %u\n", cur_live.tm.as_string(),sync);
392 cur_live.sync = sync;
393 syncpoint = true;
394 }
395
396 if (error != cur_live.error) {
397 cur_live.error = error;
398 syncpoint = true;
399 }
400
401 if (syncpoint) {
402 commit(cur_live.tm);
403
404 cur_live.tm += m_period;
405 live_delay(RUNNING_SYNCPOINT);
406 return;
407 }
408
409 cur_live.tm += m_period;
410 break;
411 }
412
413 case RUNNING_SYNCPOINT: {
414 m_write_ready(cur_live.ready);
415 m_write_sync(cur_live.sync);
416 m_write_error(cur_live.error);
417
418 cur_live.state = RUNNING;
419 checkpoint();
420 break;
421 }
422 }
423 }
424 }
425
get_next_edge(const attotime & when)426 void c2040_fdc_device::get_next_edge(const attotime &when)
427 {
428 floppy_image_device *floppy = get_floppy();
429
430 cur_live.edge = floppy ? floppy->get_next_transition(when) : attotime::never;
431 }
432
get_next_bit(attotime & tm,const attotime & limit)433 int c2040_fdc_device::get_next_bit(attotime &tm, const attotime &limit)
434 {
435 attotime next = tm + m_period;
436
437 int bit = (cur_live.edge.is_never() || cur_live.edge >= next) ? 0 : 1;
438
439 if (bit) {
440 get_next_edge(next);
441 }
442
443 return bit && cur_live.rw_sel;
444 }
445
read()446 uint8_t c2040_fdc_device::read()
447 {
448 uint8_t e = checkpoint_live.e;
449 offs_t i = checkpoint_live.i;
450
451 uint8_t data = GCR_DECODE(e, i);
452
453 if (LOG) logerror("%s %s VIA reads data %02x (%03x)\n", machine().time().as_string(), machine().describe_context(), data, checkpoint_live.shift_reg);
454
455 return data;
456 }
457
write(uint8_t data)458 void c2040_fdc_device::write(uint8_t data)
459 {
460 if (m_pi != data)
461 {
462 live_sync();
463 m_pi = cur_live.pi = data;
464 checkpoint();
465 if (LOG) logerror("%s %s PI %02x\n", machine().time().as_string(), machine().describe_context(), data);
466 live_run();
467 }
468 }
469
WRITE_LINE_MEMBER(c2040_fdc_device::ds0_w)470 WRITE_LINE_MEMBER( c2040_fdc_device::ds0_w )
471 {
472 m_ds0 = state;
473 }
474
WRITE_LINE_MEMBER(c2040_fdc_device::ds1_w)475 WRITE_LINE_MEMBER( c2040_fdc_device::ds1_w )
476 {
477 m_ds1 = state;
478
479 ds_w(m_ds1 << 1 | m_ds0);
480 }
481
WRITE_LINE_MEMBER(c2040_fdc_device::drv_sel_w)482 WRITE_LINE_MEMBER( c2040_fdc_device::drv_sel_w )
483 {
484 if (m_drv_sel != state)
485 {
486 live_sync();
487 m_drv_sel = cur_live.drv_sel = state;
488 checkpoint();
489 if (LOG) logerror("%s %s DRV SEL %u\n", machine().time().as_string(), machine().describe_context(), state);
490 live_run();
491 }
492 }
493
WRITE_LINE_MEMBER(c2040_fdc_device::mode_sel_w)494 WRITE_LINE_MEMBER( c2040_fdc_device::mode_sel_w )
495 {
496 if (m_mode_sel != state)
497 {
498 live_sync();
499 m_mode_sel = cur_live.mode_sel = state;
500 checkpoint();
501 if (LOG) logerror("%s %s MODE SEL %u\n", machine().time().as_string(), machine().describe_context(), state);
502 live_run();
503 }
504 }
505
WRITE_LINE_MEMBER(c2040_fdc_device::rw_sel_w)506 WRITE_LINE_MEMBER( c2040_fdc_device::rw_sel_w )
507 {
508 if (m_rw_sel != state)
509 {
510 live_sync();
511 m_rw_sel = cur_live.rw_sel = state;
512 checkpoint();
513 if (LOG) logerror("%s %s RW SEL %u\n", machine().time().as_string(), machine().describe_context(), state);
514 if (m_rw_sel) {
515 stop_writing(machine().time());
516 } else {
517 start_writing(machine().time());
518 }
519 live_run();
520 }
521 }
522
WRITE_LINE_MEMBER(c2040_fdc_device::mtr0_w)523 WRITE_LINE_MEMBER( c2040_fdc_device::mtr0_w )
524 {
525 if (m_mtr0 != state)
526 {
527 live_sync();
528 m_mtr0 = state;
529 if (LOG) logerror("%s %s MTR0 %u\n", machine().time().as_string(), machine().describe_context(), state);
530 m_floppy0->mon_w(state);
531 checkpoint();
532
533 if (!m_mtr0 || !m_mtr1) {
534 if(cur_live.state == IDLE) {
535 live_start();
536 }
537 } else {
538 live_abort();
539 }
540
541 live_run();
542 }
543 }
544
WRITE_LINE_MEMBER(c2040_fdc_device::mtr1_w)545 WRITE_LINE_MEMBER( c2040_fdc_device::mtr1_w )
546 {
547 if (m_mtr1 != state)
548 {
549 live_sync();
550 m_mtr1 = state;
551 if (LOG) logerror("%s %s MTR1 %u\n", machine().time().as_string(), machine().describe_context(), state);
552 if (m_floppy1) m_floppy1->mon_w(state);
553 checkpoint();
554
555 if (!m_mtr0 || !m_mtr1) {
556 if(cur_live.state == IDLE) {
557 live_start();
558 }
559 } else {
560 live_abort();
561 }
562
563 live_run();
564 }
565 }
566
stp_w(floppy_image_device * floppy,int mtr,int & old_stp,int stp)567 void c2040_fdc_device::stp_w(floppy_image_device *floppy, int mtr, int &old_stp, int stp)
568 {
569 if (mtr) return;
570
571 int tracks = 0;
572
573 switch (old_stp)
574 {
575 case 0: if (stp == 1) tracks++; else if (stp == 3) tracks--; break;
576 case 1: if (stp == 2) tracks++; else if (stp == 0) tracks--; break;
577 case 2: if (stp == 3) tracks++; else if (stp == 1) tracks--; break;
578 case 3: if (stp == 0) tracks++; else if (stp == 2) tracks--; break;
579 }
580
581 if (tracks == -1)
582 {
583 floppy->dir_w(1);
584 floppy->stp_w(1);
585 floppy->stp_w(0);
586 }
587 else if (tracks == 1)
588 {
589 floppy->dir_w(0);
590 floppy->stp_w(1);
591 floppy->stp_w(0);
592 }
593
594 old_stp = stp;
595 }
596
stp0_w(int stp)597 void c2040_fdc_device::stp0_w(int stp)
598 {
599 if (m_stp0 != stp)
600 {
601 live_sync();
602 this->stp_w(m_floppy0, m_mtr0, m_stp0, stp);
603 checkpoint();
604 live_run();
605 }
606 }
607
stp1_w(int stp)608 void c2040_fdc_device::stp1_w(int stp)
609 {
610 if (m_stp1 != stp)
611 {
612 live_sync();
613 if (m_floppy1) this->stp_w(m_floppy1, m_mtr1, m_stp1, stp);
614 checkpoint();
615 live_run();
616 }
617 }
618
ds_w(int ds)619 void c2040_fdc_device::ds_w(int ds)
620 {
621 if (m_ds != ds)
622 {
623 live_sync();
624 m_ds = cur_live.ds = ds;
625 if (LOG) logerror("%s %s DS %u\n", machine().time().as_string(), machine().describe_context(), ds);
626 checkpoint();
627 live_run();
628 }
629 }
630
set_floppy(floppy_image_device * floppy0,floppy_image_device * floppy1)631 void c2040_fdc_device::set_floppy(floppy_image_device *floppy0, floppy_image_device *floppy1)
632 {
633 m_floppy0 = floppy0;
634 m_floppy1 = floppy1;
635 }
636