1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <ctype.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "kc/system.h"
25
26 #include "kc/z80.h" // DEBUG
27
28 #include "kc/fdc.h"
29 #include "kc/fdc_cmd.h"
30 #include "kc/floppy.h"
31
32 #include "libdbg/dbg.h"
33
34 static struct sector_def
35 {
36 int MFM, N, size, SC, GPL1, GPL2;
37 } sector_def[] = {
38 { 0, 0, 128, 0x1a, 0x07, 0x1b }, /* IBM Diskette 1 */
39 { 0, 1, 256, 0x0f, 0x0e, 0x2a }, /* IBM Diskette 2 */
40 { 0, 2, 512, 0x08, 0x1b, 0x3a },
41 { 0, 3, 1024, 0x04, 0x00, 0x00 },
42 { 0, 4, 2048, 0x02, 0x00, 0x00 },
43 { 0, 5, 4096, 0x01, 0x00, 0x00 },
44 { 1, 1, 256, 0x1a, 0x0e, 0x36 }, /* IBM Diskette 2D */
45 { 1, 2, 512, 0x0f, 0x1b, 0x54 }, /* IBM Diskette 2D */
46 { 1, 3, 1024, 0x08, 0x36, 0x74 },
47 { 1, 4, 2048, 0x04, 0x00, 0x00 },
48 { 1, 5, 4096, 0x02, 0x00, 0x00 },
49 { 1, 6, 8192, 0x01, 0x00, 0x00 },
50 { -1, -1, -1, -1, -1, -1 },
51 };
52
53 static int
N_to_sector_size(int MFM,int N)54 N_to_sector_size(int MFM, int N)
55 {
56 int a;
57
58 a = 0;
59 while (sector_def[a].N != -1)
60 {
61 if ((sector_def[a].MFM == MFM) && (sector_def[a].N == N))
62 return sector_def[a].size;
63 a++;
64 }
65
66 return -1;
67 }
68
FDC_CMD(FDC * fdc,int args,int results,const char * name)69 FDC_CMD::FDC_CMD(FDC *fdc, int args, int results, const char *name)
70 {
71 _fdc = fdc;
72 _args = args;
73 _results = results;
74 _name = strdup(name);
75 _sect = new SectorDesc(0, 0);
76 }
77
~FDC_CMD(void)78 FDC_CMD::~FDC_CMD(void)
79 {
80 free(_name);
81 delete _sect;
82 }
83
get_name(void)84 const char * FDC_CMD::get_name(void)
85 {
86 return _name;
87 }
88
89 FDC *
get_fdc(void)90 FDC_CMD::get_fdc(void)
91 {
92 return _fdc;
93 }
94
95 void
execute_cmd(void)96 FDC_CMD::execute_cmd(void)
97 {
98 DBG(2, form("KCemu/FDC_CMD/command/execute",
99 "FDC: --> execute '%s'\n",
100 get_name()));
101
102 _fdc->set_state(FDC::FDC_STATE_EXECUTE);
103 _data_transfer = false;
104
105 execute();
106 if (_data_transfer)
107 _fdc->set_state(FDC::FDC_STATE_DATA);
108 else
109 finish_cmd();
110 }
111
112 void
finish_cmd(void)113 FDC_CMD::finish_cmd(void)
114 {
115 if (_results > 0)
116 _fdc->set_state(FDC::FDC_STATE_RESULT);
117 else
118 _fdc->set_state(FDC::FDC_STATE_IDLE);
119 }
120
121 void
start(byte_t val)122 FDC_CMD::start(byte_t val)
123 {
124 _w_idx = 0;
125 _r_idx = 0;
126 _arg[_w_idx++] = val;
127
128 _fdc->set_state(FDC::FDC_STATE_COMMAND);
129
130 DBG(2, form("KCemu/FDC_CMD/command/start",
131 "FDC: --> start '%s' [%02x] %d/%d\n",
132 get_name(), val, _w_idx, _args));
133
134 if (_args == 1)
135 execute_cmd();
136 }
137
138 bool
write_arg(byte_t val)139 FDC_CMD::write_arg(byte_t val)
140 {
141 _arg[_w_idx++] = val;
142
143 DBG(2, form("KCemu/FDC_CMD/command/arg",
144 "FDC: --> arg '%s' [%02x] %d/%d\n",
145 get_name(), val, _w_idx, _args));
146
147 if (_w_idx == _args)
148 execute_cmd();
149
150 return true;
151 }
152
153 byte_t
read_result(void)154 FDC_CMD::read_result(void)
155 {
156 byte_t val = 0;
157
158 val = _result[_r_idx++];
159 DBG(2, form("KCemu/FDC_CMD/command/result",
160 "FDC: <-- result '%s' - [%02x] %d/%d\n",
161 get_name(), val, _r_idx, _results));
162
163 if (_r_idx >= _results)
164 _fdc->set_state(FDC::FDC_STATE_IDLE);
165
166 return val;
167 }
168
169 byte_t
read_byte(void)170 FDC_CMD::read_byte(void)
171 {
172 DBG(1, form("KCemu/warning",
173 "FDC_CMD::read_byte() called! [current cmd is '%s']\n",
174 get_name()));
175
176 return 0xff;
177 }
178
179 void
write_byte(byte_t val)180 FDC_CMD::write_byte(byte_t val)
181 {
182 DBG(1, form("KCemu/warning",
183 "FDC_CMD::write_byte() called! [%s] (value = 0x%02x)\n",
184 get_name(), val));
185 }
186
187 int
get_read_idx(void)188 FDC_CMD::get_read_idx(void)
189 {
190 return _r_idx;
191 }
192
193 int
get_write_idx(void)194 FDC_CMD::get_write_idx(void)
195 {
196 return _w_idx;
197 }
198
199 /*
200 * INVALID
201 */
FDC_CMD_INVALID(FDC * fdc)202 FDC_CMD_INVALID::FDC_CMD_INVALID(FDC *fdc)
203 : FDC_CMD(fdc, 1, 1, "INVALID")
204 {
205 }
206
~FDC_CMD_INVALID(void)207 FDC_CMD_INVALID::~FDC_CMD_INVALID(void)
208 {
209 }
210
211 void
execute(void)212 FDC_CMD_INVALID::execute(void)
213 {
214 DBG(2, form("KCemu/FDC_CMD/INVALID",
215 "FDC: INVALID: ------------------------------------\n"
216 "FDC: INVALID: code = %02x\n"
217 "FDC: INVALID: ------------------------------------\n",
218 _arg[0]));
219
220 get_fdc()->set_ST0(FDC::ST_0_ALL_MASK, FDC::ST_0_IC_INVALID_COMMAND);
221
222 _result[0] = get_fdc()->get_ST0();
223 }
224
225 /********************************************************************
226 * 0x02 - FDC_CMD_READ_TRACK
227 */
FDC_CMD_READ_TRACK(FDC * fdc)228 FDC_CMD_READ_TRACK::FDC_CMD_READ_TRACK(FDC *fdc)
229 : FDC_CMD(fdc, 9, 7, "READ TRACK")
230 {
231 }
232
~FDC_CMD_READ_TRACK(void)233 FDC_CMD_READ_TRACK::~FDC_CMD_READ_TRACK(void)
234 {
235 }
236
237 void
execute(void)238 FDC_CMD_READ_TRACK::execute(void)
239 {
240 DBG(2, form("KCemu/FDC_CMD/READ_TRACK",
241 "FDC: READ TRACK: ---------------------------------\n"
242 "FDC: READ TRACK: ---------------------------------\n"));
243 }
244
245 /********************************************************************
246 * 0x03 - FDC_CMD_SPECIFY
247 */
FDC_CMD_SPECIFY(FDC * fdc)248 FDC_CMD_SPECIFY::FDC_CMD_SPECIFY(FDC *fdc)
249 : FDC_CMD(fdc, 3, 0, "SPECIFY")
250 {
251 }
252
~FDC_CMD_SPECIFY(void)253 FDC_CMD_SPECIFY::~FDC_CMD_SPECIFY(void)
254 {
255 }
256
257 void
execute(void)258 FDC_CMD_SPECIFY::execute(void)
259 {
260 DBG(2, form("KCemu/FDC_CMD/SPECIFY",
261 "FDC: SPECIFY: ------------------------------------\n"
262 "FDC: SPECIFY: Step Rate Time = %d ms\n"
263 "FDC: SPECIFY: Head Unload Time = %d ms\n"
264 "FDC: SPECIFY: Head Load Time = %d ms\n"
265 "FDC: SPECIFY: NON-DMA Mode = %s\n"
266 "FDC: SPECIFY: ------------------------------------\n",
267 16 - ((_arg[1] >> 4) & 0x0f),
268 (_arg[1] & 0x0f) * 16 + 16,
269 (_arg[2] & 0xfe) + 2,
270 (_arg[2] & 0x01) ? "yes" : "no (= DMA Mode)"));
271 }
272
273 /********************************************************************
274 * 0x04 - FDC_CMD_SENSE_DRIVE_STATUS
275 */
FDC_CMD_SENSE_DRIVE_STATUS(FDC * fdc)276 FDC_CMD_SENSE_DRIVE_STATUS::FDC_CMD_SENSE_DRIVE_STATUS(FDC *fdc)
277 : FDC_CMD(fdc, 2, 1, "SENSE DRIVE STATUS")
278 {
279 }
280
~FDC_CMD_SENSE_DRIVE_STATUS(void)281 FDC_CMD_SENSE_DRIVE_STATUS::~FDC_CMD_SENSE_DRIVE_STATUS(void)
282 {
283 }
284
285 void
execute(void)286 FDC_CMD_SENSE_DRIVE_STATUS::execute(void)
287 {
288 DBG(2, form("KCemu/FDC_CMD/SENSE_DRIVE_STATUS",
289 "FDC: SENSE DRIVE STATUS: -------------------------\n"
290 "FDC: SENSE DRIVE STATUS: Head Select = %d\n"
291 "FDC: SENSE DRIVE STATUS: Drive Select = %d\n"
292 "FDC: SENSE DRIVE STATUS: -------------------------\n",
293 (_arg[1] >> 2) & 1,
294 _arg[1] & 3));
295
296 get_fdc()->select_floppy(_arg[1] & 3);
297 _result[0] = get_fdc()->get_ST3();
298 }
299
300 /********************************************************************
301 * 0x05 - FDC_CMD_WRITE_DATA
302 */
FDC_CMD_WRITE_DATA(FDC * fdc)303 FDC_CMD_WRITE_DATA::FDC_CMD_WRITE_DATA(FDC *fdc)
304 : FDC_CMD(fdc, 9, 7, "WRITE DATA")
305 {
306 _buf = 0;
307 }
308
~FDC_CMD_WRITE_DATA(void)309 FDC_CMD_WRITE_DATA::~FDC_CMD_WRITE_DATA(void)
310 {
311 if (_buf != 0)
312 delete _buf;
313 }
314
315 void
execute(void)316 FDC_CMD_WRITE_DATA::execute(void)
317 {
318 DBG(2, form("KCemu/FDC_CMD/WRITE_DATA",
319 "FDC: WRITE DATA: ----------------------------------\n"
320 "FDC: WRITE DATA: Multi-Track = %s\n"
321 "FDC: WRITE DATA: FM or MFM Mode = %s\n"
322 "FDC: WRITE DATA: Skip = %s\n"
323 "FDC: WRITE DATA: Head Select = %d\n"
324 "FDC: WRITE DATA: Drive Select = %d\n"
325 "FDC: WRITE DATA: Cylinder = %d\n"
326 "FDC: WRITE DATA: Head = %d\n"
327 "FDC: WRITE DATA: Sector = %d\n"
328 "FDC: WRITE DATA: Number of Data Bytes = %d\n"
329 "FDC: WRITE DATA: End of Track = %d\n"
330 "FDC: WRITE DATA: Gap Length = %d\n"
331 "FDC: WRITE DATA: Date Length = %d\n"
332 "FDC: WRITE DATA: ----------------------------------\n",
333 ((_arg[0] >> 8) & 1) ? "yes" : "no",
334 ((_arg[0] >> 7) & 1) ? "MFM Mode" : "FM Mode",
335 ((_arg[0] >> 6) & 1) ? "yes" : "no",
336 (_arg[1] >> 2) & 1,
337 _arg[1] & 3,
338 _arg[2],
339 _arg[3],
340 _arg[4],
341 _arg[5],
342 _arg[6],
343 _arg[7],
344 _arg[8]));
345 get_fdc()->select_floppy(_arg[1] & 3);
346 get_fdc()->set_input_gate(0x40, 0x40);
347 _head = _arg[3];
348 _cylinder = _arg[2];
349 _sector = _arg[4];
350
351 if (_buf != 0)
352 delete _buf;
353 _sector_size = N_to_sector_size((_arg[0] >> 7) & 1, _arg[5]);
354 _buf = new byte_t[_sector_size];
355
356 _idx = 0;
357
358 _result[0] = get_fdc()->get_ST0();
359 _result[1] = get_fdc()->get_ST1();
360 _result[2] = get_fdc()->get_ST2();
361 _result[3] = _arg[2];
362 _result[4] = _arg[3];
363 _result[5] = _arg[4];
364 _result[6] = _arg[5];
365
366 get_fdc()->set_msr(FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM | FDC::ST_MAIN_DIO,
367 FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM);
368
369 _data_transfer = true;
370 }
371
372 void
write_byte(byte_t val)373 FDC_CMD_WRITE_DATA::write_byte(byte_t val)
374 {
375 DBG(2, form("KCemu/FDC_CMD/write_byte",
376 "FDC_CMD_WRITE_DATA::write_byte(): c/h/s %d/%d/%d [%4d]: 0x%02x (%3d, '%c')\n",
377 get_fdc()->get_cylinder(),
378 get_fdc()->get_head(),
379 get_fdc()->get_sector(),
380 _idx,
381 val, val, isprint(val) ? val : '.'));
382
383 _buf[_idx++] = val;
384 if (_idx == _sector_size)
385 {
386 Floppy *f = get_fdc()->get_floppy();
387 if (f != 0)
388 {
389 get_fdc()->seek_internal(_head, _cylinder, _sector);
390 f->write_sector(_buf, _sector_size);
391 }
392
393 if (_sector == _arg[6])
394 {
395 DBG(2, form("KCemu/FDC_CMD/WRITE_DATA",
396 "FDC: WRITE DATA: Writing sector %2d hit end of track (EOT = %d)\n",
397 _sector, _arg[6]));
398 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
399 _data_transfer = false;
400 finish_cmd();
401 }
402 }
403 }
404
405 /********************************************************************
406 * 0x06 - READ DATA
407 */
FDC_CMD_READ_DATA(FDC * fdc)408 FDC_CMD_READ_DATA::FDC_CMD_READ_DATA(FDC *fdc)
409 : FDC_CMD(fdc, 9, 7, "READ DATA")
410 {
411 _buf = 0;
412 _idx = 0;
413 _size = 0;
414 }
415
~FDC_CMD_READ_DATA(void)416 FDC_CMD_READ_DATA::~FDC_CMD_READ_DATA(void)
417 {
418 if (_buf != 0)
419 delete _buf;
420 }
421
422 void
execute(void)423 FDC_CMD_READ_DATA::execute(void)
424 {
425 Floppy *f;
426 int size, len;
427
428 DBG(2, form("KCemu/FDC_CMD/READ_DATA",
429 "FDC: READ DATA: ----------------------------------\n"
430 "FDC: READ DATA: Multi-Track = %s\n"
431 "FDC: READ DATA: FM or MFM Mode = %s\n"
432 "FDC: READ DATA: Skip = %s\n"
433 "FDC: READ DATA: Head Select = %d\n"
434 "FDC: READ DATA: Drive Select = %d\n"
435 "FDC: READ DATA: Cylinder = %d\n"
436 "FDC: READ DATA: Head = %d\n"
437 "FDC: READ DATA: Sector = %d\n"
438 "FDC: READ DATA: Number of Data Bytes = %d\n"
439 "FDC: READ DATA: End of Track = %d\n"
440 "FDC: READ DATA: Gap Length = %d\n"
441 "FDC: READ DATA: Date Length = %d\n"
442 "FDC: READ DATA: ----------------------------------\n",
443 ((_arg[0] >> 8) & 1) ? "yes" : "no",
444 ((_arg[0] >> 7) & 1) ? "MFM Mode" : "FM Mode",
445 ((_arg[0] >> 6) & 1) ? "yes" : "no",
446 (_arg[1] >> 2) & 1,
447 _arg[1] & 3,
448 _arg[2],
449 _arg[3],
450 _arg[4],
451 _arg[5],
452 _arg[6],
453 _arg[7],
454 _arg[8]));
455 get_fdc()->select_floppy(_arg[1] & 3);
456 get_fdc()->set_input_gate(0x40, 0x40);
457 get_fdc()->seek_internal(_arg[3], _arg[2], _arg[4]);
458 f = get_fdc()->get_floppy();
459 if (f == 0)
460 return;
461
462 _result[0] = get_fdc()->get_ST0();
463 _result[1] = get_fdc()->get_ST1();
464 _result[2] = get_fdc()->get_ST2();
465 _result[3] = _arg[2];
466 _result[4] = _arg[3];
467 _result[5] = _arg[4];
468 _result[6] = _arg[5];
469
470 size = f->get_sector_size();
471 if (size <= 0)
472 {
473 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
474 get_fdc()->set_ST1(FDC::ST_1_NO_DATE | FDC::ST_1_MISSING_ADDRESS_MARK,
475 FDC::ST_1_NO_DATE | FDC::ST_1_MISSING_ADDRESS_MARK);
476 _result[0] = get_fdc()->get_ST0();
477 _result[1] = get_fdc()->get_ST1();
478 return;
479 }
480
481 DBG(2, form("KCemu/FDC_CMD/READ_DATA_FORMAT",
482 "FDC: READ DATA: heads: %d\n"
483 "FDC: READ DATA: cylinders: %d\n"
484 "FDC: READ DATA: sector size: %d\n"
485 "FDC: READ DATA: sect per cyl: %d\n",
486 f->get_head_count(),
487 f->get_cylinder_count(),
488 size,
489 f->get_sectors_per_cylinder()));
490
491 if (_buf != 0)
492 delete[] _buf;
493 _buf = new byte_t[size];
494
495 len = f->read_sector(_buf, size);
496 if (len != size)
497 {
498 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
499 get_fdc()->set_ST1(FDC::ST_1_NO_DATE | FDC::ST_1_MISSING_ADDRESS_MARK,
500 FDC::ST_1_NO_DATE | FDC::ST_1_MISSING_ADDRESS_MARK);
501 _result[0] = get_fdc()->get_ST0();
502 _result[1] = get_fdc()->get_ST1();
503 return;
504 }
505
506 _idx = 0;
507 _size = size;
508
509 DBG(2, form("KCemu/FDC_CMD/READ_DATA_DUMP",
510 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
511 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
512 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
513 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
514 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
515 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
516 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n"
517 "FDC: READ DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n",
518 _buf[0x00], _buf[0x01], _buf[0x02], _buf[0x03], _buf[0x04], _buf[0x05], _buf[0x06], _buf[0x07],
519 _buf[0x08], _buf[0x09], _buf[0x0a], _buf[0x0b], _buf[0x0c], _buf[0x0d], _buf[0x0e], _buf[0x0f],
520 _buf[0x10], _buf[0x11], _buf[0x12], _buf[0x13], _buf[0x14], _buf[0x15], _buf[0x16], _buf[0x17],
521 _buf[0x18], _buf[0x19], _buf[0x1a], _buf[0x1b], _buf[0x1c], _buf[0x1d], _buf[0x1e], _buf[0x1f],
522 _buf[0x20], _buf[0x21], _buf[0x22], _buf[0x23], _buf[0x24], _buf[0x25], _buf[0x26], _buf[0x27],
523 _buf[0x28], _buf[0x29], _buf[0x2a], _buf[0x2b], _buf[0x2c], _buf[0x2d], _buf[0x2e], _buf[0x2f],
524 _buf[0x30], _buf[0x31], _buf[0x32], _buf[0x33], _buf[0x34], _buf[0x35], _buf[0x36], _buf[0x37],
525 _buf[0x38], _buf[0x39], _buf[0x3a], _buf[0x3b], _buf[0x3c], _buf[0x3d], _buf[0x3e], _buf[0x3f]));
526
527
528 get_fdc()->set_msr(FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM | FDC::ST_MAIN_DIO,
529 FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM | FDC::ST_MAIN_DIO);
530 get_fdc()->set_ST0(FDC::ST_0_IC_MASK | FDC::ST_0_SEEK_END,
531 FDC::ST_0_IC_NORMAL_TERMINATION);
532
533 _data_transfer = true;
534 }
535
536 bool
fetch_next_sector(void)537 FDC_CMD_READ_DATA::fetch_next_sector(void)
538 {
539 Floppy *f;
540 int sector, size, cnt, len;
541
542 f = get_fdc()->get_floppy();
543 if (f == 0)
544 return false;
545
546 size = f->get_sector_size();
547 if (size == 0)
548 return false;
549
550 cnt = f->get_sectors_per_cylinder();
551 sector = get_fdc()->get_sector();
552 if (sector < 0)
553 return false;
554
555 if (sector >= cnt)
556 return false;
557
558 DBG(2, form("KCemu/FDC_CMD/READ_DATA",
559 "FDC: READ DATA: Reading sector %2d\n",
560 sector));
561
562 get_fdc()->seek_internal(get_fdc()->get_head(),
563 get_fdc()->get_cylinder(),
564 sector + 1);
565 len = f->read_sector(_buf, size);
566 if (len != size)
567 return false;
568
569 _idx = 0;
570 _size = size;
571
572 _result[3] = get_fdc()->get_head();
573 _result[4] = get_fdc()->get_cylinder();
574 _result[5] = sector + 1;
575
576 return true;
577 }
578
579 byte_t
read_byte(void)580 FDC_CMD_READ_DATA::read_byte(void)
581 {
582 byte_t b = 0xff;
583
584 if (_idx == _size)
585 {
586 if (!fetch_next_sector())
587 {
588 _data_transfer = false;
589 finish_cmd();
590 }
591 }
592
593 if (_idx < _size)
594 b = _buf[_idx++];
595
596 DBG(2, form("KCemu/FDC_CMD/read_byte",
597 "FDC_CMD_READ_DATA::read_byte(): c/h/s %d/%d/%d [%4d]: 0x%02x (%3d, '%c')\n",
598 get_fdc()->get_cylinder(),
599 get_fdc()->get_head(),
600 get_fdc()->get_sector(),
601 _idx - 1,
602 b, b, isprint(b) ? b : '.'));
603
604 int sector = get_fdc()->get_sector();
605 if ((_idx == _size) && (sector == _arg[6]))
606 {
607 DBG(2, form("KCemu/FDC_CMD/READ_DATA",
608 "FDC: READ DATA: Reading sector %2d hit end of track (EOT = %d)\n",
609 sector, _arg[6]));
610 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
611 _data_transfer = false;
612 finish_cmd();
613 }
614
615 return b;
616 }
617
618 /********************************************************************
619 * 0x07 - FDC_CMD_RECALIBRATE
620 */
FDC_CMD_RECALIBRATE(FDC * fdc)621 FDC_CMD_RECALIBRATE::FDC_CMD_RECALIBRATE(FDC *fdc)
622 : FDC_CMD(fdc, 2, 0, "RECALIBRATE")
623 {
624 }
625
~FDC_CMD_RECALIBRATE(void)626 FDC_CMD_RECALIBRATE::~FDC_CMD_RECALIBRATE(void)
627 {
628 }
629
630 void
execute(void)631 FDC_CMD_RECALIBRATE::execute(void)
632 {
633 DBG(2, form("KCemu/FDC_CMD/RECALIBRATE",
634 "FDC: RECALIBRATE: --------------------------------\n"
635 "FDC: RECALIBRATE: Drive Select = %d\n"
636 "FDC: RECALIBRATE: --------------------------------\n",
637 _arg[1] & 3));
638
639 get_fdc()->select_floppy(_arg[1] & 3);
640
641 // Head retracted to Track 0, always sets SEEK END, if not track 0
642 // signal is received from the floppy the EC bit is set
643 get_fdc()->seek(get_fdc()->get_head(), 0, get_fdc()->get_sector());
644 }
645
646 /********************************************************************
647 * 0x08 - FDC_CMD_SENSE_INTERRUPT_STATUS
648 */
FDC_CMD_SENSE_INTERRUPT_STATUS(FDC * fdc)649 FDC_CMD_SENSE_INTERRUPT_STATUS::FDC_CMD_SENSE_INTERRUPT_STATUS(FDC *fdc)
650 : FDC_CMD(fdc, 1, 2, "SENSE INTERRUPT STATUS")
651 {
652 }
653
~FDC_CMD_SENSE_INTERRUPT_STATUS(void)654 FDC_CMD_SENSE_INTERRUPT_STATUS::~FDC_CMD_SENSE_INTERRUPT_STATUS(void)
655 {
656 }
657
658 void
execute(void)659 FDC_CMD_SENSE_INTERRUPT_STATUS::execute(void)
660 {
661 DBG(2, form("KCemu/FDC_CMD/SENSE_INTERRUPT_STATUS",
662 "FDC: SENSE INTERRUPT STATUS: --------------------------------\n"
663 "FDC: SENSE INTERRUPT STATUS: --------------------------------\n"
664 ));
665
666 _result[0] = get_fdc()->get_ST0();
667 _result[1] = get_fdc()->get_cylinder(); // PCN (current cylinder)
668 }
669
670 /********************************************************************
671 * 0x09 - FDC_CMD_WRITE_DELETED_DATA
672 */
FDC_CMD_WRITE_DELETED_DATA(FDC * fdc)673 FDC_CMD_WRITE_DELETED_DATA::FDC_CMD_WRITE_DELETED_DATA(FDC *fdc)
674 : FDC_CMD(fdc, 9, 7, "WRITE DELETED DATA")
675 {
676 }
677
~FDC_CMD_WRITE_DELETED_DATA(void)678 FDC_CMD_WRITE_DELETED_DATA::~FDC_CMD_WRITE_DELETED_DATA(void)
679 {
680 }
681
682 void
execute(void)683 FDC_CMD_WRITE_DELETED_DATA::execute(void)
684 {
685 DBG(2, form("KCemu/FDC_CMD/WRITE_DELETED_DATA",
686 "FDC: WRITE DELETED DATA: -------------------------\n"
687 "FDC: WRITE DELETED DATA: -------------------------\n"));
688 }
689
690 /********************************************************************
691 * 0x0a - FDC_CMD_READ_ID
692 */
FDC_CMD_READ_ID(FDC * fdc)693 FDC_CMD_READ_ID::FDC_CMD_READ_ID(FDC *fdc)
694 : FDC_CMD(fdc, 2, 7, "READ ID")
695 {
696 }
697
~FDC_CMD_READ_ID(void)698 FDC_CMD_READ_ID::~FDC_CMD_READ_ID(void)
699 {
700 }
701
702 void
execute(void)703 FDC_CMD_READ_ID::execute(void)
704 {
705 DBG(2, form("KCemu/FDC_CMD/READ_ID",
706 "FDC: READ ID: ------------------------------------\n"
707 "FDC: READ ID: FM or MFM Mode = %s\n"
708 "FDC: READ ID: Head Select = %d\n"
709 "FDC: READ ID: Drive Select = %d\n"
710 "FDC: READ ID: ------------------------------------\n",
711 ((_arg[0] >> 7) & 1) ? "MFM Mode" : "FM Mode",
712 (_arg[1] >> 2) & 1,
713 _arg[1] & 3));
714
715 get_fdc()->select_floppy(_arg[1] & 3);
716 get_fdc()->set_input_gate(0x40, 0x40);
717
718 _result[0] = get_fdc()->get_ST0();
719 _result[1] = get_fdc()->get_ST1();
720 _result[2] = get_fdc()->get_ST2();
721 _result[3] = get_fdc()->get_cylinder();
722 _result[4] = get_fdc()->get_head();
723 _result[5] = get_fdc()->get_sector();
724 _result[6] = 0x03; /* FIXME: N */
725 }
726
727 /********************************************************************
728 * 0x0c - FDC_CMD_READ_DELETED_DATA
729 */
FDC_CMD_READ_DELETED_DATA(FDC * fdc)730 FDC_CMD_READ_DELETED_DATA::FDC_CMD_READ_DELETED_DATA(FDC *fdc)
731 : FDC_CMD(fdc, 9, 7, "READ DELETED DATA")
732 {
733 }
734
~FDC_CMD_READ_DELETED_DATA(void)735 FDC_CMD_READ_DELETED_DATA::~FDC_CMD_READ_DELETED_DATA(void)
736 {
737 }
738
739 void
execute(void)740 FDC_CMD_READ_DELETED_DATA::execute(void)
741 {
742 DBG(2, form("KCemu/FDC_CMD/READ_DELETED_DATA",
743 "FDC: READ DELETED DATA: --------------------------\n"
744 "FDC: READ DELETED DATA: --------------------------\n"));
745 }
746
747 /********************************************************************
748 * 0x0d - FDC_CMD_FORMAT_A_TRACK
749 */
FDC_CMD_FORMAT_A_TRACK(FDC * fdc)750 FDC_CMD_FORMAT_A_TRACK::FDC_CMD_FORMAT_A_TRACK(FDC *fdc)
751 : FDC_CMD(fdc, 6, 7, "FORMAT A TRACK")
752 {
753 _buf = NULL;
754 }
755
~FDC_CMD_FORMAT_A_TRACK(void)756 FDC_CMD_FORMAT_A_TRACK::~FDC_CMD_FORMAT_A_TRACK(void)
757 {
758 if (_buf != NULL)
759 delete _buf;
760 }
761
762 void
execute(void)763 FDC_CMD_FORMAT_A_TRACK::execute(void)
764 {
765 DBG(2, form("KCemu/FDC_CMD/FORMAT_A_TRACK",
766 "FDC: FORMAT A TRACK: --------------------------------\n"
767 "FDC: FORMAT A TRACK: FM or MFM Mode = %s\n"
768 "FDC: FORMAT A TRACK: Head Select = %d\n"
769 "FDC: FORMAT A TRACK: Drive Select = %d\n"
770 "FDC: FORMAT A TRACK: Bytes/Sector = %d\n"
771 "FDC: FORMAT A TRACK: Sector/Cylinder = %d\n"
772 "FDC: FORMAT A TRACK: Gap 3 Length = %d\n"
773 "FDC: FORMAT A TRACK: Filler byte = %02xh (%d)\n"
774 "FDC: FORMAT A TRACK: --------------------------------\n",
775 ((_arg[0] >> 7) & 1) ? "MFM Mode" : "FM Mode",
776 (_arg[1] >> 2) & 1,
777 _arg[1] & 3,
778 _arg[2],
779 _arg[3],
780 _arg[4],
781 _arg[5], _arg[5]));
782 get_fdc()->select_floppy(_arg[1] & 3);
783 get_fdc()->set_input_gate(0x40, 0x40);
784
785 _ridx = 0;
786 _widx = 0;
787 _cur_sector = 1;
788 _formatted_sectors = 0;
789 _sectors_per_track = _arg[3];
790 if (_buf != NULL)
791 delete _buf;
792 _sector_size = N_to_sector_size((_arg[0] >> 7) & 1, _arg[2]);
793 _buf = new byte_t[_sector_size];
794 memset(_buf, _arg[5], _sector_size);
795
796 _result[0] = get_fdc()->get_ST0();
797 _result[1] = get_fdc()->get_ST1();
798 _result[2] = get_fdc()->get_ST2();
799 _result[3] = get_fdc()->get_cylinder();
800 _result[4] = get_fdc()->get_head();
801 _result[5] = get_fdc()->get_sector();
802 _result[6] = _arg[2]; // N
803
804 _data_transfer = true;
805 }
806
807 void
format(void)808 FDC_CMD_FORMAT_A_TRACK::format(void)
809 {
810 Floppy *f;
811
812 DBG(2, form("KCemu/FDC_CMD/format",
813 "FDC: FORMAT A TRACK: -sector data--------------------\n"
814 "FDC: FORMAT A TRACK: Sector %d of %d\n"
815 "FDC: FORMAT A TRACK: Head = %d\n"
816 "FDC: FORMAT A TRACK: Cylinder = %d\n"
817 "FDC: FORMAT A TRACK: Sector = %d\n"
818 "FDC: FORMAT A TRACK: Number of Data Bytes = %d\n"
819 "FDC: FORMAT A TRACK: -sector data--------------------\n",
820 _cur_sector, _sectors_per_track,
821 _head, _cylinder, _sector, _bytes_per_sector));
822
823 _ridx = 0;
824 _widx = 0;
825
826 f = get_fdc()->get_floppy();
827 if (f != 0)
828 {
829 get_fdc()->seek(_head, _cylinder, _sector);
830 f->write_sector(_buf, _sector_size);
831 }
832
833 if (_cur_sector == _sectors_per_track)
834 get_fdc()->set_input_gate(0x40, 0x00);
835
836 _cur_sector++;
837 _formatted_sectors++;
838 }
839
840 byte_t
read_byte(void)841 FDC_CMD_FORMAT_A_TRACK::read_byte(void)
842 {
843 switch (_ridx)
844 {
845 case 0:
846 _ridx++;
847 return _cylinder;
848 case 1:
849 _ridx++;
850 return _head;
851 case 2:
852 _ridx++;
853 return _sector + 1;
854 case 3:
855 _ridx = 0;
856 return _bytes_per_sector;
857 }
858
859 return 0;
860 }
861
862 void
write_byte(byte_t val)863 FDC_CMD_FORMAT_A_TRACK::write_byte(byte_t val)
864 {
865 DBG(2, form("KCemu/FDC_CMD/write_byte",
866 "FDC_CMD_FORMAT_A_TRACK::write_byte(): 0x%02x (%3d, '%c')\n",
867 val, val, isprint(val) ? val : '.'));
868
869 switch (_widx)
870 {
871 case 0:
872 _cylinder = val;
873 _widx++;
874 break;
875 case 1:
876 _head = val;
877 _widx++;
878 break;
879 case 2:
880 _sector = val;
881 _widx++;
882 break;
883 case 3:
884 _bytes_per_sector = val;
885 _widx = 0;
886 format();
887
888 if (_formatted_sectors == _sectors_per_track)
889 {
890 _data_transfer = false;
891 finish_cmd();
892 }
893
894 break;
895 }
896 }
897
898 /********************************************************************
899 * 0x0f - FDC_CMD_SEEK
900 */
FDC_CMD_SEEK(FDC * fdc)901 FDC_CMD_SEEK::FDC_CMD_SEEK(FDC *fdc)
902 : FDC_CMD(fdc, 3, 0, "SEEK")
903 {
904 }
905
~FDC_CMD_SEEK(void)906 FDC_CMD_SEEK::~FDC_CMD_SEEK(void)
907 {
908 }
909
910 void
execute(void)911 FDC_CMD_SEEK::execute(void)
912 {
913 DBG(2, form("KCemu/FDC_CMD/SEEK",
914 "FDC: SEEK: ---------------------------------------\n"
915 "FDC: SEEK: Head Select = %d\n"
916 "FDC: SEEK: Drive Select = %d\n"
917 "FDC: SEEK: New Cylinder Number = %d\n"
918 "FDC: SEEK: ---------------------------------------\n",
919 (_arg[1] >> 2) & 1,
920 _arg[1] & 3,
921 _arg[2]));
922
923 get_fdc()->select_floppy(_arg[1] & 3);
924 get_fdc()->seek((_arg[1] >> 2) & 1, _arg[2], 1);
925 }
926
927 /********************************************************************
928 * 0x11 - FDC_CMD_SCAN_EQUAL
929 */
FDC_CMD_SCAN_EQUAL(FDC * fdc)930 FDC_CMD_SCAN_EQUAL::FDC_CMD_SCAN_EQUAL(FDC *fdc)
931 : FDC_CMD(fdc, 9, 7, "SCAN EQUAL")
932 {
933 _buf = 0;
934 }
935
~FDC_CMD_SCAN_EQUAL(void)936 FDC_CMD_SCAN_EQUAL::~FDC_CMD_SCAN_EQUAL(void)
937 {
938 if (_buf != 0)
939 delete _buf;
940 }
941
942 void
execute(void)943 FDC_CMD_SCAN_EQUAL::execute(void)
944 {
945 DBG(2, form("KCemu/FDC_CMD/SCAN_EQUAL",
946 "FDC: SCAN EQUAL: ----------------------------------\n"
947 "FDC: SCAN EQUAL: Head Select = %d\n"
948 "FDC: SCAN EQUAL: Drive Select = %d\n"
949 "FDC: SCAN EQUAL: Cylinder = %d\n"
950 "FDC: SCAN EQUAL: Head = %d\n"
951 "FDC: SCAN EQUAL: Sector = %d\n"
952 "FDC: SCAN EQUAL: Number of Data Bytes = %d\n"
953 "FDC: SCAN EQUAL: End of Track = %d\n"
954 "FDC: SCAN EQUAL: Gap Length = %d\n"
955 "FDC: SCAN EQUAL: Date Length = %d\n"
956 "FDC: SCAN EQUAL: ----------------------------------\n",
957 (_arg[1] >> 2) & 1,
958 _arg[1] & 3,
959 _arg[2],
960 _arg[3],
961 _arg[4],
962 _arg[5],
963 _arg[6],
964 _arg[7],
965 _arg[8]));
966
967 get_fdc()->select_floppy(_arg[1] & 3);
968 get_fdc()->set_input_gate(0x40, 0x40);
969
970 _head = _arg[3];
971 _cylinder = _arg[2];
972 _sector = _arg[4];
973 get_fdc()->seek_internal(_head, _cylinder, _sector);
974
975 Floppy *f = get_fdc()->get_floppy();
976 if (f == NULL)
977 return;
978
979 _result[0] = get_fdc()->get_ST0();
980 _result[1] = get_fdc()->get_ST1();
981 _result[2] = get_fdc()->get_ST2();
982 _result[3] = _arg[2];
983 _result[4] = _arg[3];
984 _result[5] = _arg[4];
985 _result[6] = _arg[5];
986
987 get_fdc()->set_ST2(FDC::ST_2_SCAN_MASK, 0);
988
989 int size = f->get_sector_size();
990 if (size <= 0)
991 {
992 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
993 _result[0] = get_fdc()->get_ST0();
994 _result[1] = get_fdc()->get_ST1();
995 return;
996 }
997
998 if (_buf != 0)
999 delete _buf;
1000 _buf = new byte_t[size];
1001
1002 int len = f->read_sector(_buf, size);
1003 if (len != size)
1004 {
1005 get_fdc()->set_ST0(FDC::ST_0_IC_MASK, FDC::ST_0_IC_ABNORMAL_TERMINATION);
1006 _result[0] = get_fdc()->get_ST0();
1007 _result[1] = get_fdc()->get_ST1();
1008 return;
1009 }
1010
1011 _idx = 0;
1012 _sector_size = size;
1013
1014 get_fdc()->set_msr(FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM | FDC::ST_MAIN_DIO,
1015 FDC::ST_MAIN_READ_WRITE | FDC::ST_MAIN_RQM);
1016
1017 _data_transfer = true;
1018 }
1019
1020 void
write_byte(byte_t val)1021 FDC_CMD_SCAN_EQUAL::write_byte(byte_t val)
1022 {
1023 byte_t b = _buf[_idx];
1024 DBG(2, form("KCemu/FDC_CMD/write_byte",
1025 "FDC_CMD_SCAN_EQUAL::write_byte(): c/h/s %d/%d/%d [%4d]: "
1026 "0x%02x (%3d, '%c') == 0x%02x (%3d, '%c')\n",
1027 get_fdc()->get_cylinder(),
1028 get_fdc()->get_head(),
1029 get_fdc()->get_sector(),
1030 _idx,
1031 val, val, isprint(val) ? val : '.',
1032 b, b, isprint(b) ? b : '.'));
1033
1034 if (b != val)
1035 {
1036 get_fdc()->set_ST2(FDC::ST_2_SCAN_MASK, FDC::ST_2_SCAN_NOT_SATISFIED);
1037 _result[0] = get_fdc()->get_ST0();
1038 _result[1] = get_fdc()->get_ST1();
1039 _result[2] = get_fdc()->get_ST2();
1040 _data_transfer = false;
1041 finish_cmd();
1042 }
1043
1044 _idx++;
1045
1046 if (_idx == _sector_size)
1047 {
1048 get_fdc()->set_ST2(FDC::ST_2_SCAN_MASK, FDC::ST_2_SCAN_EQUAL_HIT);
1049 _result[0] = get_fdc()->get_ST0();
1050 _result[1] = get_fdc()->get_ST1();
1051 _result[2] = get_fdc()->get_ST2();
1052 _data_transfer = false;
1053 finish_cmd();
1054 }
1055 }
1056
1057 /********************************************************************
1058 * 0x19 - FDC_CMD_SCAN_LOW_OR_EQUAL
1059 */
FDC_CMD_SCAN_LOW_OR_EQUAL(FDC * fdc)1060 FDC_CMD_SCAN_LOW_OR_EQUAL::FDC_CMD_SCAN_LOW_OR_EQUAL(FDC *fdc)
1061 : FDC_CMD(fdc, 9, 7, "SCAN LOW OR EQUAL")
1062 {
1063 }
1064
~FDC_CMD_SCAN_LOW_OR_EQUAL(void)1065 FDC_CMD_SCAN_LOW_OR_EQUAL::~FDC_CMD_SCAN_LOW_OR_EQUAL(void)
1066 {
1067 }
1068
1069 void
execute(void)1070 FDC_CMD_SCAN_LOW_OR_EQUAL::execute(void)
1071 {
1072 DBG(2, form("KCemu/FDC_CMD/SCAN_LOW_OR_EQUAL",
1073 "FDC: SCAN LOW OR EQUAL: --------------------------\n"
1074 "FDC: SCAN LOW OR EQUAL: --------------------------\n"));
1075 }
1076
1077 /********************************************************************
1078 * 0x1d - FDC_CMD_SCAN_HIGH_OR_EQUAL
1079 */
FDC_CMD_SCAN_HIGH_OR_EQUAL(FDC * fdc)1080 FDC_CMD_SCAN_HIGH_OR_EQUAL::FDC_CMD_SCAN_HIGH_OR_EQUAL(FDC *fdc)
1081 : FDC_CMD(fdc, 9, 7, "SCAN HIGH OR EQUAL")
1082 {
1083 }
1084
~FDC_CMD_SCAN_HIGH_OR_EQUAL(void)1085 FDC_CMD_SCAN_HIGH_OR_EQUAL::~FDC_CMD_SCAN_HIGH_OR_EQUAL(void)
1086 {
1087 }
1088
1089 void
execute(void)1090 FDC_CMD_SCAN_HIGH_OR_EQUAL::execute(void)
1091 {
1092 DBG(2, form("KCemu/FDC_CMD/SCAN_HIGH_OR_EQUAL",
1093 "FDC: SCAN HIGH OR EQUAL: -------------------------\n"
1094 "FDC: SCAN HIGH OR EQUAL: --------------------------\n"));
1095 }
1096