1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /*********************************************************************
4 
5     wozfdc.c
6 
7     Implementation of the Apple Disk II floppy disk controller
8 
9 *********************************************************************/
10 
11 #include "emu.h"
12 #include "wozfdc.h"
13 
14 #include "imagedev/floppy.h"
15 #include "formats/ap2_dsk.h"
16 
17 /***************************************************************************
18     PARAMETERS
19 ***************************************************************************/
20 
21 //**************************************************************************
22 //  GLOBAL VARIABLES
23 //**************************************************************************
24 
25 DEFINE_DEVICE_TYPE(DISKII_FDC,   diskii_fdc_device,   "d2fdc", "Apple Disk II floppy controller")
26 DEFINE_DEVICE_TYPE(APPLEIII_FDC, appleiii_fdc_device, "a3fdc", "Apple III floppy controller")
27 
28 #define DISKII_P6_REGION  "diskii_rom_p6"
29 
ROM_START(diskiing)30 ROM_START( diskiing )
31 	ROM_REGION(0x100, DISKII_P6_REGION, 0)
32 	ROM_LOAD( "341-0028-a.rom", 0x0000, 0x0100, CRC(b72a2c70) SHA1(bc39fbd5b9a8d2287ac5d0a42e639fc4d3c2f9d4)) /* 341-0028: 16-sector disk drive (older version), PROM P6 */
33 ROM_END
34 
35 //-------------------------------------------------
36 //  rom_region - device-specific ROM region
37 //-------------------------------------------------
38 
39 const tiny_rom_entry *wozfdc_device::device_rom_region() const
40 {
41 	return ROM_NAME( diskiing );
42 }
43 
44 //-------------------------------------------------
45 //  device_add_mconfig - add device configuration
46 //-------------------------------------------------
47 
device_add_mconfig(machine_config & config)48 void wozfdc_device::device_add_mconfig(machine_config &config)
49 {
50 	F9334(config, m_phaselatch); // 9334 on circuit diagram but 74LS259 in parts list; actual chip may vary
51 	m_phaselatch->parallel_out_cb().set(FUNC(wozfdc_device::set_phase));
52 }
53 
54 //**************************************************************************
55 //  LIVE DEVICE
56 //**************************************************************************
57 
wozfdc_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)58 wozfdc_device::wozfdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
59 	device_t(mconfig, type, tag, owner, clock),
60 		m_phaselatch(*this, "phaselatch")
61 {
62 }
63 
diskii_fdc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)64 diskii_fdc_device::diskii_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
65 	wozfdc_device(mconfig, DISKII_FDC, tag, owner, clock)
66 {
67 }
68 
appleiii_fdc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)69 appleiii_fdc_device::appleiii_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
70 	wozfdc_device(mconfig, APPLEIII_FDC, tag, owner, clock)
71 {
72 }
73 
74 //-------------------------------------------------
75 //  device_start - device-specific startup
76 //-------------------------------------------------
77 
device_start()78 void wozfdc_device::device_start()
79 {
80 	m_rom_p6 = machine().root_device().memregion(this->subtag(DISKII_P6_REGION).c_str())->base();
81 
82 	timer = timer_alloc(0);
83 	delay_timer = timer_alloc(1);
84 
85 	save_item(NAME(last_6502_write));
86 	save_item(NAME(mode_write));
87 	save_item(NAME(mode_load));
88 	save_item(NAME(active));
89 	save_item(NAME(external_io_select));
90 	save_item(NAME(cycles));
91 	save_item(NAME(data_reg));
92 	save_item(NAME(address));
93 	save_item(NAME(write_start_time));
94 	save_item(NAME(write_position));
95 	save_item(NAME(write_line_active));
96 	save_item(NAME(drvsel));
97 	save_item(NAME(enable1));
98 }
99 
device_reset()100 void wozfdc_device::device_reset()
101 {
102 	floppy = nullptr;
103 	active = MODE_IDLE;
104 	mode_write = false;
105 	mode_load = false;
106 	last_6502_write = 0x00;
107 	cycles = time_to_cycles(machine().time());
108 	data_reg = 0x00;
109 	address = 0x00;
110 	write_start_time = attotime::never;
111 	write_position = 0;
112 	write_line_active = false;
113 	external_io_select = false;
114 
115 	// Just a timer to be sure that the lss is updated from time to
116 	// time, so that there's no hiccup when it's talked to again.
117 	timer->adjust(attotime::from_msec(10), 0, attotime::from_msec(10));
118 }
119 
a3_update_drive_sel()120 void wozfdc_device::a3_update_drive_sel()
121 {
122 	floppy_image_device *newflop = nullptr;
123 
124 	if (!external_io_select)
125 	{
126 		newflop = floppy0->get_device();
127 	}
128 	else
129 	{
130 		switch (drvsel & 3)
131 		{
132 			case 0:
133 				newflop = floppy0->get_device();
134 				break;
135 
136 			case 1:
137 				newflop = floppy1->get_device();
138 				break;
139 
140 			case 2:
141 				newflop = floppy2->get_device();
142 				break;
143 
144 			case 3:
145 				newflop = floppy3->get_device();
146 				break;
147 		}
148 	}
149 
150 	if (floppy != newflop)
151 	{
152 		if(active) {
153 			lss_sync();
154 			floppy->mon_w(true);
155 		}
156 		floppy = newflop;
157 		if(active)
158 			floppy->mon_w(false);
159 	}
160 }
161 
device_reset()162 void diskii_fdc_device::device_reset()
163 {
164 	wozfdc_device::device_reset();
165 	external_drive_select = false;
166 
167 	if (floppy0 != nullptr)
168 	{
169 		floppy = floppy0->get_device();
170 	}
171 }
172 
device_reset()173 void appleiii_fdc_device::device_reset()
174 {
175 	wozfdc_device::device_reset();
176 	external_drive_select = true;
177 	drvsel = 0;
178 	enable1 = 1;
179 }
180 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)181 void wozfdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
182 {
183 	if(active)
184 		lss_sync();
185 
186 	if(id == 1 && active == MODE_DELAY) {
187 		if(floppy)
188 			floppy->mon_w(true);
189 		active = MODE_IDLE;
190 	}
191 }
192 
193 /*-------------------------------------------------
194     read - called to read the FDC's registers
195 -------------------------------------------------*/
196 
read(offs_t offset)197 uint8_t wozfdc_device::read(offs_t offset)
198 {
199 	lss_sync();
200 	control(offset);
201 
202 	if(!(offset & 1)) {
203 		return data_reg;
204 	}
205 	return 0xff;
206 }
207 
208 
209 /*-------------------------------------------------
210     write - called to write the FDC's registers
211 -------------------------------------------------*/
212 
write(offs_t offset,uint8_t data)213 void wozfdc_device::write(offs_t offset, uint8_t data)
214 {
215 	lss_sync();
216 	control(offset);
217 	last_6502_write = data;
218 }
219 
set_phase(uint8_t data)220 void wozfdc_device::set_phase(uint8_t data)
221 {
222 	if (floppy && active)
223 		floppy->seek_phase_w(data);
224 }
225 
control(int offset)226 void wozfdc_device::control(int offset)
227 {
228 	if(offset < 8)
229 		m_phaselatch->write_bit(offset >> 1, offset & 1);
230 
231 	else
232 		switch(offset) {
233 		case 0x8:
234 			if(active == MODE_ACTIVE) {
235 				delay_timer->adjust(attotime::from_seconds(1));
236 				active = MODE_DELAY;
237 			}
238 			break;
239 		case 0x9:
240 			switch(active) {
241 			case MODE_IDLE:
242 				if(floppy)
243 					floppy->mon_w(false);
244 				active = MODE_ACTIVE;
245 				if(floppy)
246 					lss_start();
247 				break;
248 			case MODE_DELAY:
249 				active = MODE_ACTIVE;
250 				delay_timer->adjust(attotime::never);
251 				break;
252 			}
253 			break;
254 		case 0xa:
255 				external_io_select = false;
256 				if(floppy != floppy0->get_device()) {
257 					if(active)
258 						floppy->mon_w(true);
259 				floppy = floppy0->get_device();
260 				if(active)
261 					floppy->mon_w(false);
262 			}
263 			break;
264 		case 0xb:
265 			external_io_select = true;
266 			if (!external_drive_select)
267 			{
268 				if (floppy != floppy1->get_device())
269 				{
270 					if(active)
271 						floppy->mon_w(true);
272 					floppy = floppy1->get_device();
273 					if(active)
274 						floppy->mon_w(false);
275 				}
276 			}
277 			else
278 			{
279 				a3_update_drive_sel();
280 			}
281 			break;
282 		case 0xc:
283 			if(mode_load) {
284 				if(active)
285 					address &= ~0x04;
286 				mode_load = false;
287 			}
288 			break;
289 		case 0xd:
290 			if(!mode_load) {
291 				if(active)
292 					address |= 0x04;
293 				mode_load = true;
294 			}
295 			break;
296 		case 0xe:
297 			if(mode_write) {
298 				if(active)
299 					address &= ~0x08;
300 				mode_write = false;
301 				attotime now = machine().time();
302 				if(floppy)
303 					floppy->write_flux(write_start_time, now, write_position, write_buffer);
304 			}
305 			break;
306 		case 0xf:
307 			if(!mode_write) {
308 				if(active) {
309 					address |= 0x08;
310 					write_start_time = machine().time();
311 					write_position = 0;
312 					if(floppy)
313 						floppy->set_write_splice(write_start_time);
314 				}
315 				mode_write = true;
316 			}
317 			break;
318 		}
319 }
320 
time_to_cycles(const attotime & tm)321 uint64_t wozfdc_device::time_to_cycles(const attotime &tm)
322 {
323 	// Clock is falling edges of the ~2Mhz clock
324 	// The 1021800 must be the controlling 6502's speed
325 
326 	uint64_t cycles = tm.as_ticks(clock()*2);
327 	cycles = (cycles+1) >> 1;
328 	return cycles;
329 }
330 
cycles_to_time(uint64_t cycles)331 attotime wozfdc_device::cycles_to_time(uint64_t cycles)
332 {
333 	return attotime::from_ticks(cycles*2+1, clock()*2);
334 }
335 
lss_start()336 void wozfdc_device::lss_start()
337 {
338 	cycles = time_to_cycles(machine().time());
339 	data_reg = 0x00;
340 	address &= ~0x0e;
341 	write_position = 0;
342 	write_start_time = mode_write ? machine().time() : attotime::never;
343 	write_line_active = false;
344 	if(mode_write && floppy)
345 		floppy->set_write_splice(write_start_time);
346 }
347 
lss_sync()348 void wozfdc_device::lss_sync()
349 {
350 	if(!active)
351 		return;
352 
353 	attotime next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles-1)) : attotime::never;
354 
355 	uint64_t cycles_limit = time_to_cycles(machine().time());
356 	uint64_t cycles_next_flux = next_flux != attotime::never ? time_to_cycles(next_flux) : uint64_t(-1);
357 	uint64_t cycles_next_flux_down = cycles_next_flux != uint64_t(-1) ? cycles_next_flux+1 : uint64_t(-1);
358 
359 	if(cycles >= cycles_next_flux && cycles < cycles_next_flux_down)
360 		address &= ~0x10;
361 	else
362 		address |= 0x10;
363 
364 	while(cycles < cycles_limit) {
365 		uint64_t cycles_next_trans = cycles_limit;
366 		if(cycles_next_trans > cycles_next_flux && cycles < cycles_next_flux)
367 			cycles_next_trans = cycles_next_flux;
368 		if(cycles_next_trans > cycles_next_flux_down && cycles < cycles_next_flux_down)
369 			cycles_next_trans = cycles_next_flux_down;
370 
371 		while(cycles < cycles_next_trans) {
372 			uint8_t opcode = m_rom_p6[address];
373 
374 			if(mode_write) {
375 				if((write_line_active && !(address & 0x80)) ||
376 					(!write_line_active && (address & 0x80))) {
377 					write_line_active = !write_line_active;
378 					assert(write_position != 32);
379 					write_buffer[write_position++] = cycles_to_time(cycles);
380 				} else if(write_position >= 30) {
381 					attotime now = cycles_to_time(cycles);
382 					if(floppy)
383 						floppy->write_flux(write_start_time, now, write_position, write_buffer);
384 					write_start_time = now;
385 					write_position = 0;
386 				}
387 			}
388 
389 			address = (address & 0x1e) | (opcode & 0xc0) | ((opcode & 0x20) >> 5) | ((opcode & 0x10) << 1);
390 			switch(opcode & 0x0f) {
391 			case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
392 				data_reg = 0x00;
393 				break;
394 			case 0x8: case 0xc:
395 				break;
396 			case 0x9:
397 				data_reg <<= 1;
398 				break;
399 			case 0xa: case 0xe:
400 				data_reg = (data_reg >> 1) | (floppy && floppy->wpt_r() ? 0x80 : 0x00);
401 				break;
402 			case 0xb: case 0xf:
403 				data_reg = last_6502_write;
404 				break;
405 			case 0xd:
406 				data_reg = (data_reg << 1) | 0x01;
407 				break;
408 			}
409 			if(data_reg & 0x80)
410 				address |= 0x02;
411 			else
412 				address &= ~0x02;
413 			cycles++;
414 		}
415 
416 		if(cycles == cycles_next_flux)
417 			address &= ~0x10;
418 		else if(cycles == cycles_next_flux_down) {
419 			address |= 0x10;
420 			next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles)) : attotime::never;
421 			cycles_next_flux = next_flux != attotime::never ? time_to_cycles(next_flux) : uint64_t(-1);
422 			cycles_next_flux_down = cycles_next_flux != uint64_t(-1) ? cycles_next_flux+1 : uint64_t(-1);
423 		}
424 	}
425 }
426 
427 // set the two images for the Disk II
set_floppies(floppy_connector * f0,floppy_connector * f1)428 void diskii_fdc_device::set_floppies(floppy_connector *f0, floppy_connector *f1)
429 {
430 	floppy0 = f0;
431 	floppy1 = f1;
432 
433 	if (floppy0)
434 	{
435 		floppy = floppy0->get_device();
436 	}
437 }
438 
set_floppies_4(floppy_connector * f0,floppy_connector * f1,floppy_connector * f2,floppy_connector * f3)439 void appleiii_fdc_device::set_floppies_4(floppy_connector *f0, floppy_connector *f1, floppy_connector *f2, floppy_connector *f3)
440 {
441 	floppy0 = f0;
442 	floppy1 = f1;
443 	floppy2 = f2;
444 	floppy3 = f3;
445 
446 	if (floppy0)
447 	{
448 		floppy = floppy0->get_device();
449 	}
450 }
451 
read_c0dx(uint8_t offset)452 uint8_t appleiii_fdc_device::read_c0dx(uint8_t offset)
453 {
454 	control_dx(offset);
455 
456 	return 0xff;
457 }
458 
write_c0dx(uint8_t offset,uint8_t data)459 void appleiii_fdc_device::write_c0dx(uint8_t offset, uint8_t data)
460 {
461 	control_dx(offset);
462 }
463 
control_dx(int offset)464 void appleiii_fdc_device::control_dx(int offset)
465 {
466 	switch (offset)
467 	{
468 		case 0: // clear drive select bit 0
469 			drvsel &= ~1;
470 			break;
471 
472 		case 1: // set drive select bit 0
473 			drvsel |= 1;
474 			break;
475 
476 		case 2: // clear drive select bit 1
477 			drvsel &= ~2;
478 			break;
479 
480 		case 3: // set drive select bit 1
481 			drvsel |= 2;
482 			break;
483 
484 		case 4: // clear enable 1
485 			enable1 = 0;
486 			break;
487 
488 		case 5: // set enable 1
489 			enable1 = 1;
490 			break;
491 
492 		case 6: // clear side 2
493 		case 7: // set side 2
494 			break;
495 
496 		default:    // cod8-c0df are not FDC related
497 			break;
498 	}
499 
500 	if (offset < 8)
501 	{
502 		a3_update_drive_sel();
503 	}
504 }
505