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