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