OSDN Git Service

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