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