OSDN Git Service

[VM][DISK][UI][Qt] "Ignore CRC Errors" per drive.
[csp-qt/common_source_project-fm7.git] / source / src / vm / mb8877.cpp
1 /*
2         Skelton for retropc emulator
3
4         Origin : XM7
5         Author : Takeda.Toshiya
6         Date   : 2006.12.06 -
7
8         [ MB8877 / MB8876 ]
9 */
10
11 #include "mb8877.h"
12 #include "disk.h"
13
14 #define FDC_ST_BUSY             0x01    // busy
15 #define FDC_ST_INDEX            0x02    // index hole
16 #define FDC_ST_DRQ              0x02    // data request
17 #define FDC_ST_TRACK00          0x04    // track0
18 #define FDC_ST_LOSTDATA         0x04    // data lost
19 #define FDC_ST_CRCERR           0x08    // crc error
20 #define FDC_ST_SEEKERR          0x10    // seek error
21 #define FDC_ST_RECNFND          0x10    // sector not found
22 #define FDC_ST_HEADENG          0x20    // head engage
23 #define FDC_ST_RECTYPE          0x20    // record type
24 #define FDC_ST_WRITEFAULT       0x20    // write fault
25 #define FDC_ST_WRITEP           0x40    // write protectdc
26 #define FDC_ST_NOTREADY         0x80    // media not inserted
27
28 #define FDC_CMD_TYPE1           1
29 #define FDC_CMD_RD_SEC          2
30 #define FDC_CMD_RD_MSEC         3
31 #define FDC_CMD_WR_SEC          4
32 #define FDC_CMD_WR_MSEC         5
33 #define FDC_CMD_RD_ADDR         6
34 #define FDC_CMD_RD_TRK          7
35 #define FDC_CMD_WR_TRK          8
36 #define FDC_CMD_TYPE4           0x80
37
38 #define EVENT_SEEK              0
39 #define EVENT_SEEKEND           1
40 #define EVENT_SEARCH            2
41 #define EVENT_TYPE4             3
42 #define EVENT_DRQ               4
43 #define EVENT_MULTI1            5
44 #define EVENT_MULTI2            6
45 #define EVENT_LOST              7
46
47 #define DRIVE_MASK              (MAX_DRIVE - 1)
48
49 static const int seek_wait_hi[4] = {3000,  6000, 10000, 16000};
50 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000};
51
52 #define CANCEL_EVENT(event) { \
53         if(register_id[event] != -1) { \
54                 cancel_event(this, register_id[event]); \
55                 register_id[event] = -1; \
56         } \
57 }
58 #define REGISTER_EVENT(event, usec) { \
59         if(register_id[event] != -1) { \
60                 cancel_event(this, register_id[event]); \
61                 register_id[event] = -1; \
62         } \
63         register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, &register_id[event]); \
64 }
65 #define REGISTER_SEEK_EVENT() { \
66         if(register_id[EVENT_SEEK] != -1) { \
67                 cancel_event(this, register_id[EVENT_SEEK]); \
68                 register_id[EVENT_SEEK] = -1; \
69         } \
70         if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) { \
71                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, &register_id[EVENT_SEEK]); \
72         } else { \
73                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, &register_id[EVENT_SEEK]); \
74         } \
75         now_seek = after_seek = true; \
76 }
77 #define REGISTER_DRQ_EVENT() { \
78         double usec = disk[drvreg]->get_usec_per_bytes(1) - passed_usec(prev_drq_clock); \
79         if(usec < 4) { \
80                 usec = 4; \
81         } else if(usec > 24 && disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_ALPHA) { \
82                 usec = 24; \
83         } \
84         if(register_id[EVENT_DRQ] != -1) { \
85                 cancel_event(this, register_id[EVENT_DRQ]); \
86                 register_id[EVENT_DRQ] = -1; \
87         } \
88         register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, &register_id[EVENT_DRQ]); \
89 }
90 #define REGISTER_LOST_EVENT() { \
91         if(register_id[EVENT_LOST] != -1) { \
92                 cancel_event(this, register_id[EVENT_LOST]); \
93                 register_id[EVENT_LOST] = -1; \
94         } \
95         register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(/*1*/2), false, &register_id[EVENT_LOST]); \
96 }
97
98 void MB8877::initialize()
99 {
100         // initialize d88 handler
101         for(int i = 0; i < MAX_DRIVE; i++) {
102                 disk[i] = new DISK(emu);
103         }
104         
105         // initialize timing
106         memset(fdc, 0, sizeof(fdc));
107         
108         // initialize fdc
109         seektrk = 0;
110         seekvct = true;
111         status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
112         drvreg = 0;
113 }
114
115 void MB8877::release()
116 {
117         // release d88 handler
118         for(int i = 0; i < MAX_DRIVE; i++) {
119                 if(disk[i]) {
120                         disk[i]->close();
121                         delete disk[i];
122                 }
123         }
124 }
125
126 void MB8877::reset()
127 {
128         for(int i = 0; i < MAX_DRIVE; i++) {
129                 fdc[i].track = 0;
130                 fdc[i].index = 0;
131                 fdc[i].access = false;
132         }
133         for(int i = 0; i < array_length(register_id); i++) {
134                 register_id[i] = -1;
135         }
136         now_search = now_seek = after_seek = drive_sel = false;
137         no_command = 0;
138 }
139
140 void MB8877::write_io8(uint32 addr, uint32 data)
141 {
142         switch(addr & 3) {
143         case 0:
144                 // command reg
145                 cmdreg_tmp = cmdreg;
146 #ifdef HAS_MB8876
147                 cmdreg = (~data) & 0xff;
148 #else
149                 cmdreg = data;
150 #endif
151                 process_cmd();
152                 no_command = 0;
153                 break;
154         case 1:
155                 // track reg
156 #ifdef HAS_MB8876
157                 trkreg = (~data) & 0xff;
158 #else
159                 trkreg = data;
160 #endif
161                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
162                         // track reg is written after command starts
163                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
164                                 process_cmd();
165                         }
166                 }
167                 break;
168         case 2:
169                 // sector reg
170 #ifdef HAS_MB8876
171                 secreg = (~data) & 0xff;
172 #else
173                 secreg = data;
174 #endif
175                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
176                         // sector reg is written after command starts
177                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
178                                 process_cmd();
179                         }
180                 }
181                 break;
182         case 3:
183                 // data reg
184 #ifdef HAS_MB8876
185                 datareg = (~data) & 0xff;
186 #else
187                 datareg = data;
188 #endif
189                 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
190                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
191                                 // write or multisector write
192                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
193                                         if(!disk[drvreg]->write_protected) {
194                                                 disk[drvreg]->sector[fdc[drvreg].index] = datareg;
195                                                 // dm, ddm
196                                                 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
197                                         } else {
198                                                 status |= FDC_ST_WRITEFAULT;
199                                                 status &= ~FDC_ST_BUSY;
200                                                 cmdtype = 0;
201                                                 set_irq(true);
202                                         }
203                                         fdc[drvreg].index++;
204                                 }
205                                 if(fdc[drvreg].index >= disk[drvreg]->sector_size.sd) {
206                                         if(cmdtype == FDC_CMD_WR_SEC) {
207                                                 // single sector
208                                                 status &= ~FDC_ST_BUSY;
209                                                 cmdtype = 0;
210                                                 set_irq(true);
211                                         } else {
212                                                 // multisector
213                                                 REGISTER_EVENT(EVENT_MULTI1, 30);
214                                                 REGISTER_EVENT(EVENT_MULTI2, 60);
215                                         }
216                                 } else if(status & FDC_ST_DRQ) {
217                                         REGISTER_DRQ_EVENT();
218                                 }
219                                 status &= ~FDC_ST_DRQ;
220                         } else if(cmdtype == FDC_CMD_WR_TRK) {
221                                 // write track
222                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
223                                         if(!disk[drvreg]->write_protected) {
224                                                 if(fdc[drvreg].index == 0) {
225                                                         disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
226                                                         fdc[drvreg].id_written = false;
227                                                         fdc[drvreg].side = sidereg;
228                                                         fdc[drvreg].side_changed = false;
229                                                 }
230                                                 if(fdc[drvreg].side != sidereg) {
231                                                         fdc[drvreg].side_changed = true;
232                                                 }
233                                                 if(fdc[drvreg].side_changed) {
234                                                         // abort write track because disk side is changed
235                                                 } else if(datareg == 0xf5) {
236                                                         // write a1h in missing clock
237                                                 } else if(datareg == 0xf6) {
238                                                         // write c2h in missing clock
239                                                 } else if(datareg == 0xf7) {
240                                                         // write crc
241                                                         if(!fdc[drvreg].id_written) {
242                                                                 uint8 c, h, r, n;
243                                                                 c = h = r = n = 0;
244                                                                 // insert new sector with crc error
245                                                                 fdc[drvreg].id_written = true;
246                                                                 fdc[drvreg].sector_found = false;
247                                                                 if (fdc[drvreg].index >= 4) {
248                                                                         c = disk[drvreg]->track[fdc[drvreg].index - 4];
249                                                                         h = disk[drvreg]->track[fdc[drvreg].index - 3];
250                                                                         r = disk[drvreg]->track[fdc[drvreg].index - 2];
251                                                                         n = disk[drvreg]->track[fdc[drvreg].index - 1];
252                                                                 }
253                                                                 fdc[drvreg].sector_length = 0x80 << (n & 3);
254                                                                 fdc[drvreg].sector_index = 0;
255                                                                 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
256                                                         } else {
257                                                                 // clear crc error if all sector data are written
258                                                                 if(fdc[drvreg].sector_found && fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
259                                                                         disk[drvreg]->set_crc_error(false);
260                                                                 }
261                                                                 fdc[drvreg].id_written = false;
262                                                         }
263                                                 } else if(fdc[drvreg].id_written) {
264                                                         if(fdc[drvreg].sector_found) {
265                                                                 // sector data
266                                                                 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
267                                                                         disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
268                                                                 }
269                                                                 fdc[drvreg].sector_index++;
270                                                         } else if(datareg == 0xf8 || datareg == 0xfb) {
271                                                                 // data mark
272                                                                 disk[drvreg]->set_deleted(datareg == 0xf8);
273                                                                 fdc[drvreg].sector_found = true;
274                                                         }
275                                                 }
276                                                 disk[drvreg]->track[fdc[drvreg].index] = datareg;
277                                         } else {
278                                                 status |= FDC_ST_WRITEFAULT;
279                                                 status &= ~FDC_ST_BUSY;
280                                                 status &= ~FDC_ST_DRQ;
281                                                 cmdtype = 0;
282                                                 set_irq(true);
283                                         }
284                                         fdc[drvreg].index++;
285                                 }
286                                 if(fdc[drvreg].index >= disk[drvreg]->get_track_size()) {
287                                         status &= ~FDC_ST_BUSY;
288                                         cmdtype = 0;
289                                         if(!disk[drvreg]->write_protected) {
290                                                 disk[drvreg]->sync_buffer();
291                                         }
292                                         set_irq(true);
293                                 } else if(status & FDC_ST_DRQ) {
294                                         REGISTER_DRQ_EVENT();
295                                 }
296                                 status &= ~FDC_ST_DRQ;
297                         }
298                         if(!(status & FDC_ST_DRQ)) {
299                                 CANCEL_EVENT(EVENT_LOST);
300                                 set_drq(false);
301                                 fdc[drvreg].access = true;
302                         }
303                 }
304                 break;
305         }
306 }
307
308 uint32 MB8877::read_io8(uint32 addr)
309 {
310         uint32 val;
311         
312         switch(addr & 3) {
313         case 0:
314                 // status reg
315                 if(cmdtype == FDC_CMD_TYPE4) {
316                         // now force interrupt
317                         if(!disk[drvreg]->inserted || !motor_on) {
318                                 status = FDC_ST_NOTREADY;
319                         } else {
320                                 // MZ-2500 RELICS invites STATUS = 0
321                                 status = 0;
322                         }
323                         val = status;
324                 } else if(now_search) {
325                         // now sector search
326                         val = FDC_ST_BUSY;
327                 } else {
328                         // disk not inserted, motor stop
329                         if(!disk[drvreg]->inserted || !motor_on) {
330                                 status |= FDC_ST_NOTREADY;
331                         } else {
332                                 status &= ~FDC_ST_NOTREADY;
333                         }
334                         // write protected
335                         if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
336                                 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
337                                         status |= FDC_ST_WRITEP;
338                                 } else {
339                                         status &= ~FDC_ST_WRITEP;
340                                 }
341                         } else {
342                                 status &= ~FDC_ST_WRITEP;
343                         }
344                         // track0, index hole
345                         if(cmdtype == FDC_CMD_TYPE1) {
346                                 if(fdc[drvreg].track == 0) {
347                                         status |= FDC_ST_TRACK00;
348                                 } else {
349                                         status &= ~FDC_ST_TRACK00;
350                                 }
351                                 if(!(status & FDC_ST_NOTREADY)) {
352                                         if(get_cur_position() == 0) {
353                                                 status |= FDC_ST_INDEX;
354                                         } else {
355                                                 status &= ~FDC_ST_INDEX;
356                                         }
357                                 }
358                         }
359                         // show busy a moment
360                         val = status;
361                         if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
362                                 status &= ~FDC_ST_BUSY;
363                         }
364                 }
365                 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
366                         // MZ-2000 HuBASIC invites NOT READY status
367                         if(++no_command == 16) {
368                                 val |= FDC_ST_NOTREADY;
369                         }
370                 } else {
371                         no_command = 0;
372                 }
373                 // reset irq/drq
374                 if(!(status & FDC_ST_DRQ)) {
375                         set_drq(false);
376                 }
377                 set_irq(false);
378 #ifdef _FDC_DEBUG_LOG
379                 emu->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
380 #endif
381 #ifdef HAS_MB8876
382                 return (~val) & 0xff;
383 #else
384                 return val;
385 #endif
386         case 1:
387                 // track reg
388 #ifdef HAS_MB8876
389                 return (~trkreg) & 0xff;
390 #else
391                 return trkreg;
392 #endif
393         case 2:
394                 // sector reg
395 #ifdef HAS_MB8876
396                 return (~secreg) & 0xff;
397 #else
398                 return secreg;
399 #endif
400         case 3:
401                 // data reg
402                 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
403                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
404                                 // read or multisector read
405                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
406                                         datareg = disk[drvreg]->sector[fdc[drvreg].index];
407                                         fdc[drvreg].index++;
408                                 }
409                                 if(fdc[drvreg].index >= disk[drvreg]->sector_size.sd) {
410                                         if(cmdtype == FDC_CMD_RD_SEC) {
411                                                 // single sector
412 #ifdef _FDC_DEBUG_LOG
413                                                 emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
414 #endif
415                                                 status &= ~FDC_ST_BUSY;
416                                                 cmdtype = 0;
417                                                 set_irq(true);
418                                         } else {
419                                                 // multisector
420 #ifdef _FDC_DEBUG_LOG
421                                                 emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
422 #endif
423                                                 REGISTER_EVENT(EVENT_MULTI1, 30);
424                                                 REGISTER_EVENT(EVENT_MULTI2, 60);
425                                         }
426                                 } else {
427                                         REGISTER_DRQ_EVENT();
428                                 }
429                                 status &= ~FDC_ST_DRQ;
430                         } else if(cmdtype == FDC_CMD_RD_ADDR) {
431                                 // read address
432                                 if(fdc[drvreg].index < 6) {
433                                         datareg = disk[drvreg]->id[fdc[drvreg].index];
434                                         fdc[drvreg].index++;
435                                 }
436                                 if(fdc[drvreg].index >= 6) {
437                                         status &= ~FDC_ST_BUSY;
438                                         cmdtype = 0;
439                                         set_irq(true);
440                                 } else {
441                                         REGISTER_DRQ_EVENT();
442                                 }
443                                 status &= ~FDC_ST_DRQ;
444                         } else if(cmdtype == FDC_CMD_RD_TRK) {
445                                 // read track
446                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
447                                         datareg = disk[drvreg]->track[fdc[drvreg].index];
448                                         fdc[drvreg].index++;
449                                 }
450                                 if(fdc[drvreg].index >= disk[drvreg]->get_track_size()) {
451 #ifdef _FDC_DEBUG_LOG
452                                         emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));
453 #endif
454                                         status &= ~FDC_ST_BUSY;
455                                         status |= FDC_ST_LOSTDATA;
456                                         cmdtype = 0;
457                                         set_irq(true);
458                                 } else {
459                                         REGISTER_DRQ_EVENT();
460                                 }
461                                 status &= ~FDC_ST_DRQ;
462                         }
463                         if(!(status & FDC_ST_DRQ)) {
464                                 CANCEL_EVENT(EVENT_LOST);
465                                 set_drq(false);
466                                 fdc[drvreg].access = true;
467                         }
468                 }
469 #ifdef _FDC_DEBUG_LOG
470                 emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
471 #endif
472 #ifdef HAS_MB8876
473                 return (~datareg) & 0xff;
474 #else
475                 return datareg;
476 #endif
477         }
478         return 0xff;
479 }
480
481 void MB8877::write_dma_io8(uint32 addr, uint32 data)
482 {
483         write_io8(3, data);
484 }
485
486 uint32 MB8877::read_dma_io8(uint32 addr)
487 {
488         return read_io8(3);
489 }
490
491 void MB8877::write_signal(int id, uint32 data, uint32 mask)
492 {
493         if(id == SIG_MB8877_DRIVEREG) {
494                 drvreg = data & DRIVE_MASK;
495                 drive_sel = true;
496         } else if(id == SIG_MB8877_SIDEREG) {
497                 sidereg = (data & mask) ? 1 : 0;
498         } else if(id == SIG_MB8877_MOTOR) {
499                 motor_on = ((data & mask) != 0);
500         }
501 }
502
503 uint32 MB8877::read_signal(int ch)
504 {
505         // get access status
506         uint32 stat = 0;
507         for(int i = 0; i < MAX_DRIVE; i++) {
508                 if(fdc[i].access) {
509                         stat |= 1 << i;
510                 }
511                 fdc[i].access = false;
512         }
513         if(now_search) {
514                 stat |= 1 << drvreg;
515         }
516         return stat;
517 }
518
519 void MB8877::event_callback(int event_id, int err)
520 {
521         int event = event_id >> 8;
522         int cmd = event_id & 0xff;
523         register_id[event] = -1;
524         
525         // cancel event if the command is finished or other command is executed
526         if(cmd != cmdtype) {
527                 if(event == EVENT_SEEK) {
528                         now_seek = false;
529                 } else if(event == EVENT_SEARCH) {
530                         now_search = false;
531                 }
532                 return;
533         }
534         
535         switch(event) {
536         case EVENT_SEEK:
537                 if(seektrk > fdc[drvreg].track) {
538                         fdc[drvreg].track++;
539                 } else if(seektrk < fdc[drvreg].track) {
540                         fdc[drvreg].track--;
541                 }
542                 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
543                         trkreg = fdc[drvreg].track;
544                 }
545                 if(seektrk == fdc[drvreg].track) {
546                         // auto update
547                         if((cmdreg & 0xf0) == 0) {
548                                 datareg = 0;
549                         }
550                         status |= search_track();
551                         now_seek = false;
552                         set_irq(true);
553                 } else {
554                         REGISTER_SEEK_EVENT();
555                 }
556                 break;
557         case EVENT_SEEKEND:
558                 if(seektrk == fdc[drvreg].track) {
559                         // auto update
560                         if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
561                                 trkreg = fdc[drvreg].track;
562                         }
563                         if((cmdreg & 0xf0) == 0) {
564                                 datareg = 0;
565                         }
566                         status |= search_track();
567                         now_seek = false;
568                         CANCEL_EVENT(EVENT_SEEK);
569                         set_irq(true);
570                 }
571                 break;
572         case EVENT_SEARCH:
573                 now_search = false;
574                 if(!(status_tmp & FDC_ST_RECNFND)) {
575                         status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
576                         REGISTER_LOST_EVENT();
577                         fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
578                         fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
579                         set_drq(true);
580                         drive_sel = false;
581                 } else {
582 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
583                         // for SHARP X1 Batten Tanuki
584                         if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
585                                 status_tmp &= ~FDC_ST_RECNFND;
586                         }
587 #endif
588                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
589                 }
590                 break;
591         case EVENT_TYPE4:
592                 cmdtype = FDC_CMD_TYPE4;
593                 break;
594         case EVENT_DRQ:
595                 if(status & FDC_ST_BUSY) {
596                         status |= FDC_ST_DRQ;
597                         REGISTER_LOST_EVENT();
598                         fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
599                         fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
600                         set_drq(true);
601                 }
602                 break;
603         case EVENT_MULTI1:
604                 secreg++;
605                 break;
606         case EVENT_MULTI2:
607                 if(cmdtype == FDC_CMD_RD_MSEC) {
608                         cmd_readdata();
609                 } else if(cmdtype == FDC_CMD_WR_MSEC) {
610                         cmd_writedata();
611                 }
612                 break;
613         case EVENT_LOST:
614                 if(status & FDC_ST_BUSY) {
615 #ifdef _FDC_DEBUG_LOG
616                         emu->out_debug_log("FDC\tDATA LOST\n");
617 #endif
618                         status |= FDC_ST_LOSTDATA;
619                         status &= ~FDC_ST_BUSY;
620                         //status &= ~FDC_ST_DRQ;
621                         set_irq(true);
622                         //set_drq(false);
623                 }
624                 break;
625         }
626 }
627
628 // ----------------------------------------------------------------------------
629 // command
630 // ----------------------------------------------------------------------------
631
632 void MB8877::process_cmd()
633 {
634 #ifdef _FDC_DEBUG_LOG
635         static const _TCHAR *cmdstr[0x10] = {
636                 _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),
637                 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
638                 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
639                 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
640         };
641         emu->out_debug_log(_T("FDC\tCMD=%2xh (%s) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, cmdstr[cmdreg >> 4], datareg, drvreg, trkreg, sidereg, secreg);
642 #endif
643         
644         CANCEL_EVENT(EVENT_TYPE4);
645         set_irq(false);
646         
647         switch(cmdreg & 0xf0) {
648         // type-1
649         case 0x00:
650                 cmd_restore();
651                 break;
652         case 0x10:
653                 cmd_seek();
654                 break;
655         case 0x20:
656         case 0x30:
657                 cmd_step();
658                 break;
659         case 0x40:
660         case 0x50:
661                 cmd_stepin();
662                 break;
663         case 0x60:
664         case 0x70:
665                 cmd_stepout();
666                 break;
667         // type-2
668         case 0x80:
669         case 0x90:
670                 cmd_readdata();
671                 break;
672         case 0xa0:
673         case 0xb0:
674                 cmd_writedata();
675                 break;
676         // type-3
677         case 0xc0:
678                 cmd_readaddr();
679                 break;
680         case 0xe0:
681                 cmd_readtrack();
682                 break;
683         case 0xf0:
684                 cmd_writetrack();
685                 break;
686         // type-4
687         case 0xd0:
688                 cmd_forceint();
689                 break;
690         default:
691                 break;
692         }
693 }
694
695 void MB8877::cmd_restore()
696 {
697         // type-1 restore
698         cmdtype = FDC_CMD_TYPE1;
699         status = FDC_ST_HEADENG | FDC_ST_BUSY;
700         trkreg = 0xff;
701         
702         seektrk = 0;
703         seekvct = true;
704         
705         REGISTER_SEEK_EVENT();
706         REGISTER_EVENT(EVENT_SEEKEND, 300);
707 }
708
709 void MB8877::cmd_seek()
710 {
711         // type-1 seek
712         cmdtype = FDC_CMD_TYPE1;
713         status = FDC_ST_HEADENG | FDC_ST_BUSY;
714         
715 #if 0
716         seektrk = fdc[drvreg].track + datareg - trkreg;
717 #else
718         seektrk = datareg;
719 #endif
720         seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
721         seekvct = !(datareg > trkreg);
722         
723         REGISTER_SEEK_EVENT();
724         REGISTER_EVENT(EVENT_SEEKEND, 300);
725 }
726
727 void MB8877::cmd_step()
728 {
729         // type-1 step
730         if(seekvct) {
731                 cmd_stepout();
732         } else {
733                 cmd_stepin();
734         }
735 }
736
737 void MB8877::cmd_stepin()
738 {
739         // type-1 step in
740         cmdtype = FDC_CMD_TYPE1;
741         status = FDC_ST_HEADENG | FDC_ST_BUSY;
742         
743         seektrk = (fdc[drvreg].track < 83) ? fdc[drvreg].track + 1 : 83;
744         seekvct = false;
745         
746         REGISTER_SEEK_EVENT();
747         REGISTER_EVENT(EVENT_SEEKEND, 300);
748 }
749
750 void MB8877::cmd_stepout()
751 {
752         // type-1 step out
753         cmdtype = FDC_CMD_TYPE1;
754         status = FDC_ST_HEADENG | FDC_ST_BUSY;
755         
756         seektrk = (fdc[drvreg].track > 0) ? fdc[drvreg].track - 1 : 0;
757         seekvct = true;
758         
759         REGISTER_SEEK_EVENT();
760         REGISTER_EVENT(EVENT_SEEKEND, 300);
761 }
762
763 void MB8877::cmd_readdata()
764 {
765         // type-2 read data
766         cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
767         int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;
768         status = FDC_ST_BUSY;
769         status_tmp = search_sector(fdc[drvreg].track, side, secreg, ((cmdreg & 2) != 0));
770         now_search = true;
771         
772         double time;
773         if(!(status_tmp & FDC_ST_RECNFND)) {
774                 time = get_usec_to_start_trans();
775         } else {
776                 time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());
777         }
778         REGISTER_EVENT(EVENT_SEARCH, time);
779         CANCEL_EVENT(EVENT_LOST);
780 }
781
782 void MB8877::cmd_writedata()
783 {
784         // type-2 write data
785         cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
786         int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;
787         status = FDC_ST_BUSY;
788         status_tmp = search_sector(fdc[drvreg].track, side, secreg, ((cmdreg & 2) != 0)) & ~FDC_ST_RECTYPE;
789         now_search = true;
790         
791         double time;
792         if(!(status_tmp & FDC_ST_RECNFND)) {
793                 time = get_usec_to_start_trans();
794         } else {
795                 time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());
796         }
797         REGISTER_EVENT(EVENT_SEARCH, time);
798         CANCEL_EVENT(EVENT_LOST);
799 }
800
801 void MB8877::cmd_readaddr()
802 {
803         // type-3 read address
804         cmdtype = FDC_CMD_RD_ADDR;
805         status = FDC_ST_BUSY;
806         status_tmp = search_addr();
807         now_search = true;
808         
809         double time;
810         if(!(status_tmp & FDC_ST_RECNFND)) {
811                 time = get_usec_to_start_trans();
812         } else {
813                 time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());
814         }
815         REGISTER_EVENT(EVENT_SEARCH, time);
816         CANCEL_EVENT(EVENT_LOST);
817 }
818
819 void MB8877::cmd_readtrack()
820 {
821         // type-3 read track
822         cmdtype = FDC_CMD_RD_TRK;
823         status = FDC_ST_BUSY;
824         status_tmp = 0;
825         
826         disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
827         fdc[drvreg].index = 0;
828         now_search = true;
829         
830         int bytes = disk[drvreg]->get_track_size() - get_cur_position();
831         if(bytes < 12) {
832                 bytes += disk[drvreg]->get_track_size();
833         }
834         double time = disk[drvreg]->get_usec_per_bytes(bytes);
835         REGISTER_EVENT(EVENT_SEARCH, time);
836         CANCEL_EVENT(EVENT_LOST);
837 }
838
839 void MB8877::cmd_writetrack()
840 {
841         // type-3 write track
842         cmdtype = FDC_CMD_WR_TRK;
843         status = FDC_ST_BUSY;
844         status_tmp = 0;
845         
846         fdc[drvreg].index = 0;
847         now_search = true;
848         
849         int bytes = disk[drvreg]->get_track_size() - get_cur_position();
850         if(bytes < 12) {
851                 bytes += disk[drvreg]->get_track_size();
852         }
853         double time = disk[drvreg]->get_usec_per_bytes(bytes);
854         REGISTER_EVENT(EVENT_SEARCH, time);
855         CANCEL_EVENT(EVENT_LOST);
856 }
857
858 void MB8877::cmd_forceint()
859 {
860         // type-4 force interrupt
861 #if 0
862         if(!disk[drvreg]->inserted || !motor_on) {
863                 status = FDC_ST_NOTREADY | FDC_ST_HEADENG;
864         } else {
865                 status = FDC_ST_HEADENG;
866         }
867         cmdtype = FDC_CMD_TYPE4;
868 #else
869 //      if(cmdtype == 0 || cmdtype == 4) {              // modified for mz-2800, why in the write sector command case?
870         if(cmdtype == 0 || cmdtype == FDC_CMD_TYPE4) {  // is this correct?
871                 status = 0;
872                 cmdtype = FDC_CMD_TYPE1;
873         }
874         status &= ~FDC_ST_BUSY;
875 #endif
876         
877         // force interrupt if bit0-bit3 is high
878         if(cmdreg & 0x0f) {
879                 set_irq(true);
880         }
881         
882         // finish current seeking
883         if(now_seek) {
884                 if(seektrk > fdc[drvreg].track) {
885                         fdc[drvreg].track++;
886                 } else if(seektrk < fdc[drvreg].track) {
887                         fdc[drvreg].track--;
888                 }
889                 if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
890                         trkreg = fdc[drvreg].track;
891                 }
892                 if(seektrk == fdc[drvreg].track) {
893                         // auto update
894                         if((cmdreg_tmp & 0xf0) == 0) {
895                                 datareg = 0;
896                         }
897                 }
898         }
899         now_search = now_seek = false;
900         
901         CANCEL_EVENT(EVENT_SEEK);
902         CANCEL_EVENT(EVENT_SEEKEND);
903         CANCEL_EVENT(EVENT_SEARCH);
904         CANCEL_EVENT(EVENT_TYPE4);
905         CANCEL_EVENT(EVENT_DRQ);
906         CANCEL_EVENT(EVENT_MULTI1);
907         CANCEL_EVENT(EVENT_MULTI2);
908         CANCEL_EVENT(EVENT_LOST);
909         REGISTER_EVENT(EVENT_TYPE4, 100);
910 }
911
912 // ----------------------------------------------------------------------------
913 // media handler
914 // ----------------------------------------------------------------------------
915
916 uint8 MB8877::search_track()
917 {
918         int trk = fdc[drvreg].track;
919         int trkside = trk * 2 + (sidereg & 1);
920         
921         if(!(disk[drvreg]->inserted && disk[drvreg]->check_media_type())) {
922                 return FDC_ST_SEEKERR;
923         }
924         if(!(0 <= trkside && trkside < 164)) {
925                 return FDC_ST_SEEKERR;
926         }
927         
928         // verify track number
929         disk[drvreg]->get_track(trk, sidereg);
930         if(!(cmdreg & 4)) {
931                 return 0;
932         }
933         for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
934                 disk[drvreg]->get_sector(trk, sidereg, i);
935                 if(disk[drvreg]->id[0] == trkreg) {
936                         return 0;
937                 }
938         }
939         return FDC_ST_SEEKERR;
940 }
941
942 uint8 MB8877::search_sector(int trk, int side, int sct, bool compare)
943 {
944         // get track
945         if(!disk[drvreg]->get_track(trk, side)) {
946                 set_irq(true);
947                 return FDC_ST_RECNFND;
948         }
949         
950         // get current position
951         int sector_num = disk[drvreg]->sector_num.sd;
952         int position = get_cur_position();
953         
954         if(position > disk[drvreg]->sync_position[sector_num - 1]) {
955                 position -= disk[drvreg]->get_track_size();
956         }
957         
958         // first scanned sector
959         int first_sector = 0;
960         for(int i = 0; i < sector_num; i++) {
961                 if(position < disk[drvreg]->sync_position[i]) {
962                         first_sector = i;
963                         break;
964                 }
965         }
966         
967         // scan sectors
968         for(int i = 0; i < sector_num; i++) {
969                 // get sector
970                 int index = (first_sector + i) % sector_num;
971                 disk[drvreg]->get_sector(trk, side, index);
972                 
973                 // check id
974                 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
975                         continue;
976                 }
977                 if(disk[drvreg]->id[2] != sct) {
978                         continue;
979                 }
980                 
981                 // sector found
982                 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i];
983                 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[i];
984                 fdc[drvreg].index = 0;
985                 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0) | ((disk[drvreg]->crc_error && !config.ignore_crc[drvreg]) ? FDC_ST_CRCERR : 0);
986         }
987         
988         // sector not found
989         disk[drvreg]->sector_size.sd = 0;
990         set_irq(true);
991         return FDC_ST_RECNFND;
992 }
993
994 uint8 MB8877::search_addr()
995 {
996         int trk = fdc[drvreg].track;
997         
998         // get track
999         if(!disk[drvreg]->get_track(trk, sidereg)) {
1000                 set_irq(true);
1001                 return FDC_ST_RECNFND;
1002         }
1003         
1004         // get current position
1005         int sector_num = disk[drvreg]->sector_num.sd;
1006         int position = get_cur_position();
1007         
1008         if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1009                 position -= disk[drvreg]->get_track_size();
1010         }
1011         
1012         // first scanned sector
1013         int first_sector = 0;
1014         for(int i = 0; i < sector_num; i++) {
1015                 if(position < disk[drvreg]->sync_position[i]) {
1016                         first_sector = i;
1017                         break;
1018                 }
1019         }
1020         
1021         // get sector
1022         if(disk[drvreg]->get_sector(trk, sidereg, first_sector)) {
1023                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector];
1024                 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[first_sector];
1025                 fdc[drvreg].index = 0;
1026                 secreg = disk[drvreg]->id[0];
1027                 return (disk[drvreg]->crc_error && !config.ignore_crc[drvreg]) ? FDC_ST_CRCERR : 0;
1028         }
1029         
1030         // sector not found
1031         disk[drvreg]->sector_size.sd = 0;
1032         set_irq(true);
1033         return FDC_ST_RECNFND;
1034 }
1035
1036 // ----------------------------------------------------------------------------
1037 // timing
1038 // ----------------------------------------------------------------------------
1039
1040 int MB8877::get_cur_position()
1041 {
1042         return (int)(fdc[drvreg].cur_position + passed_usec(fdc[drvreg].prev_clock) / disk[drvreg]->get_usec_per_bytes(1)) % disk[drvreg]->get_track_size();
1043 }
1044
1045 double MB8877::get_usec_to_start_trans()
1046 {
1047 #if defined(_X1TURBO) || defined(_X1TURBOZ)
1048         // FIXME: ugly patch for X1turbo ALPHA
1049         if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_ALPHA) {
1050                 return 100;
1051         } else
1052 #endif
1053         if(disk[drvreg]->no_skew) {
1054                 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1055                 // so use the constant period to search the target sector
1056                 return 50000;
1057         }
1058         
1059         // get time from current position
1060         int position = get_cur_position();
1061         int bytes = fdc[drvreg].next_trans_position - position;
1062         if(fdc[drvreg].next_sync_position < position) {
1063                 bytes += disk[drvreg]->get_track_size();
1064         }
1065         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1066         if(after_seek) {
1067                 // wait 70msec to read/write data just after seek command is done
1068 //              if(time < 70000) {
1069 //                      time += disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());
1070 //              }
1071                 after_seek = false;
1072         }
1073         return time;
1074 }
1075
1076 // ----------------------------------------------------------------------------
1077 // irq / drq
1078 // ----------------------------------------------------------------------------
1079
1080 void MB8877::set_irq(bool val)
1081 {
1082         write_signals(&outputs_irq, val ? 0xffffffff : 0);
1083 }
1084
1085 void MB8877::set_drq(bool val)
1086 {
1087         write_signals(&outputs_drq, val ? 0xffffffff : 0);
1088 }
1089
1090 // ----------------------------------------------------------------------------
1091 // user interface
1092 // ----------------------------------------------------------------------------
1093
1094 void MB8877::open_disk(int drv, _TCHAR path[], int bank)
1095 {
1096         if(drv < MAX_DRIVE) {
1097                 disk[drv]->open(path, bank);
1098         }
1099 }
1100
1101 void MB8877::close_disk(int drv)
1102 {
1103         if(drv < MAX_DRIVE) {
1104                 disk[drv]->close();
1105                 cmdtype = 0;
1106         }
1107 }
1108
1109 bool MB8877::disk_inserted(int drv)
1110 {
1111         if(drv < MAX_DRIVE) {
1112                 return disk[drv]->inserted;
1113         }
1114         return false;
1115 }
1116
1117 void MB8877::set_drive_type(int drv, uint8 type)
1118 {
1119         if(drv < MAX_DRIVE) {
1120                 disk[drv]->drive_type = type;
1121         }
1122 }
1123
1124 uint8 MB8877::get_drive_type(int drv)
1125 {
1126         if(drv < MAX_DRIVE) {
1127                 return disk[drv]->drive_type;
1128         }
1129         return DRIVE_TYPE_UNK;
1130 }
1131
1132 void MB8877::set_drive_rpm(int drv, int rpm)
1133 {
1134         if(drv < MAX_DRIVE) {
1135                 disk[drv]->drive_rpm = rpm;
1136         }
1137 }
1138
1139 void MB8877::set_drive_mfm(int drv, bool mfm)
1140 {
1141         if(drv < MAX_DRIVE) {
1142                 disk[drv]->drive_mfm = mfm;
1143         }
1144 }
1145
1146 uint8 MB8877::fdc_status()
1147 {
1148         // for each virtual machines
1149 #if defined(_FMR50) || defined(_FMR60)
1150         return disk[drvreg]->inserted ? 2 : 0;
1151 #else
1152         return 0;
1153 #endif
1154 }
1155
1156 #define STATE_VERSION   3
1157
1158 void MB8877::save_state(FILEIO* state_fio)
1159 {
1160         state_fio->FputUint32(STATE_VERSION);
1161         state_fio->FputInt32(this_device_id);
1162         
1163         state_fio->Fwrite(fdc, sizeof(fdc), 1);
1164         for(int i = 0; i < MAX_DRIVE; i++) {
1165                 disk[i]->save_state(state_fio);
1166         }
1167         state_fio->FputUint8(status);
1168         state_fio->FputUint8(status_tmp);
1169         state_fio->FputUint8(cmdreg);
1170         state_fio->FputUint8(cmdreg_tmp);
1171         state_fio->FputUint8(trkreg);
1172         state_fio->FputUint8(secreg);
1173         state_fio->FputUint8(datareg);
1174         state_fio->FputUint8(drvreg);
1175         state_fio->FputUint8(sidereg);
1176         state_fio->FputUint8(cmdtype);
1177         state_fio->Fwrite(register_id, sizeof(register_id), 1);
1178         state_fio->FputBool(now_search);
1179         state_fio->FputBool(now_seek);
1180         state_fio->FputBool(after_seek);
1181         state_fio->FputInt32(no_command);
1182         state_fio->FputInt32(seektrk);
1183         state_fio->FputBool(seekvct);
1184         state_fio->FputBool(motor_on);
1185         state_fio->FputBool(drive_sel);
1186         state_fio->FputUint32(prev_drq_clock);
1187 }
1188
1189 bool MB8877::load_state(FILEIO* state_fio)
1190 {
1191         if(state_fio->FgetUint32() != STATE_VERSION) {
1192                 return false;
1193         }
1194         if(state_fio->FgetInt32() != this_device_id) {
1195                 return false;
1196         }
1197         state_fio->Fread(fdc, sizeof(fdc), 1);
1198         for(int i = 0; i < MAX_DRIVE; i++) {
1199                 if(!disk[i]->load_state(state_fio)) {
1200                         return false;
1201                 }
1202         }
1203         status = state_fio->FgetUint8();
1204         status_tmp = state_fio->FgetUint8();
1205         cmdreg = state_fio->FgetUint8();
1206         cmdreg_tmp = state_fio->FgetUint8();
1207         trkreg = state_fio->FgetUint8();
1208         secreg = state_fio->FgetUint8();
1209         datareg = state_fio->FgetUint8();
1210         drvreg = state_fio->FgetUint8();
1211         sidereg = state_fio->FgetUint8();
1212         cmdtype = state_fio->FgetUint8();
1213         state_fio->Fread(register_id, sizeof(register_id), 1);
1214         now_search = state_fio->FgetBool();
1215         now_seek = state_fio->FgetBool();
1216         after_seek = state_fio->FgetBool();
1217         no_command = state_fio->FgetInt32();
1218         seektrk = state_fio->FgetInt32();
1219         seekvct = state_fio->FgetBool();
1220         motor_on = state_fio->FgetBool();
1221         drive_sel = state_fio->FgetBool();
1222         prev_drq_clock = state_fio->FgetUint32();
1223         return true;
1224 }
1225