OSDN Git Service

[VM][MB8877] Fix verify timing on SEEK command.Fix not booting Sylpheed for FM77AV.
[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 / MB89311 ]
9 */
10
11 #include "mb8877.h"
12 #include "disk.h"
13 #include "noise.h"
14
15 #define FDC_ST_BUSY             0x01    // busy
16 #define FDC_ST_INDEX            0x02    // index hole
17 #define FDC_ST_DRQ              0x02    // data request
18 #define FDC_ST_TRACK00          0x04    // track0
19 #define FDC_ST_LOSTDATA         0x04    // data lost
20 #define FDC_ST_CRCERR           0x08    // crc error
21 #define FDC_ST_SEEKERR          0x10    // seek error
22 #define FDC_ST_RECNFND          0x10    // sector not found
23 #define FDC_ST_HEADENG          0x20    // head engage
24 #define FDC_ST_RECTYPE          0x20    // record type
25 #define FDC_ST_WRITEFAULT       0x20    // write fault
26 #define FDC_ST_WRITEP           0x40    // write protectdc
27 #define FDC_ST_NOTREADY         0x80    // media not inserted
28
29 #define FDC_CMD_TYPE1           1
30 #define FDC_CMD_RD_SEC          2
31 #define FDC_CMD_RD_MSEC         3
32 #define FDC_CMD_WR_SEC          4
33 #define FDC_CMD_WR_MSEC         5
34 #define FDC_CMD_RD_ADDR         6
35 #define FDC_CMD_RD_TRK          7
36 #define FDC_CMD_WR_TRK          8
37
38 #define EVENT_SEEK                              0
39 #define EVENT_SEEKEND_VERIFY    1
40 #define EVENT_SEARCH                    2
41 #define EVENT_DRQ                               3
42 #define EVENT_MULTI1                    4
43 #define EVENT_MULTI2                    5
44 #define EVENT_LOST                              6
45 #define EVENT_END_READ_SECTOR   7
46 #define EVENT_CRC_ERROR         8
47
48 //#define DRIVE_MASK            (MAX_DRIVE - 1)
49
50 #define DELAY_TIME              (disk[drvreg]->drive_type == DRIVE_TYPE_2HD ? 15000 : 30000)
51
52 static const int seek_wait_hi[4] = {3000,  6000, 10000, 16000}; // 2MHz
53 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000}; // 1MHz
54
55 void MB8877::cancel_my_event(int event)
56 {
57         if(register_id[event] != -1) {
58                 cancel_event(this, register_id[event]);
59                 register_id[event] = -1;
60         }
61 }
62
63 bool MB8877::register_my_event_with_check(int event, double usec)
64 {
65         if(register_id[event] >= 0) return false;
66         register_my_event(event, usec);
67         return true;
68 }
69
70 void MB8877::register_my_event(int event, double usec)
71 {
72         cancel_my_event(event);
73         register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, &register_id[event]);
74 }
75
76 void MB8877::register_seek_event()
77 {
78         cancel_my_event(EVENT_SEEK);
79         if(fdc[drvreg].track == seektrk) {
80                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), 300, false, &register_id[EVENT_SEEK]); // From XM7 v3.4L77a Wait 300uS.
81         } else if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
82                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, &register_id[EVENT_SEEK]);
83         } else {
84                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, &register_id[EVENT_SEEK]);
85         }
86         now_seek = true;
87 }
88
89 void MB8877::register_drq_event(int bytes)
90 {
91         double nusec = disk[drvreg]->get_usec_per_bytes(bytes); // For debug
92         double usec = nusec - get_passed_usec(prev_drq_clock);
93         bool nf = false;
94         
95         if(usec > nusec) usec = nusec; 
96         if(usec < 4) {
97                 usec = 4;
98         }
99         if(config.correct_disk_timing[drvreg]) {
100                 nf = true;
101         }
102         if(type_fm7) {
103                 if((disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER)) {
104                    /* (disk[drvreg]->is_special_disk == SPECIAL_DISK_FM77AV_PSYOBLADE) || */
105                         nf = true;
106                 }
107         }
108         if(nf) {
109                 switch(disk[drvreg]->media_type) {
110                 case MEDIA_TYPE_2D:
111                 case MEDIA_TYPE_2DD:
112                         if(usec > 7.0) usec = 7.0;
113                         break;
114                 case MEDIA_TYPE_2HD:
115                 case MEDIA_TYPE_144:
116                 case MEDIA_TYPE_UNK:
117                         usec = 4.0;
118                         break;
119                 default:
120                         usec = 4.0;
121                         break;
122                 }
123         }
124         if(fdc_debug_log) this->out_debug_log("DRQ REG: %dbytes %d:%d -> %f\n", bytes, get_current_clock(), prev_drq_clock, usec);
125         cancel_my_event(EVENT_DRQ);
126         register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, &register_id[EVENT_DRQ]);
127 }
128
129 void MB8877::register_lost_event(int bytes)
130 {
131         cancel_my_event(EVENT_LOST);
132         register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, &register_id[EVENT_LOST]);
133 }
134
135 bool MB8877::check_drive(void)
136 {
137         if(drvreg >= _max_drive) {
138                 status = FDC_ST_NOTREADY;
139                 //set_drq(false);
140                 set_irq(true);
141                 return false;
142         }
143         return true;
144 }
145
146 bool MB8877::check_drive2(void)
147 {
148         if(!check_drive()) {
149                 return false;
150         }
151         if(!is_disk_inserted(drvreg & 0x7f)) {
152                 status = FDC_ST_NOTREADY;
153                 set_irq(true);
154                 //set_drq(false);
155                 return false;
156         }
157         return true;
158 }
159
160 void MB8877::initialize()
161 {
162         DEVICE::initialize();
163         // initialize d88 handler
164         if(osd->check_feature(_T("MAX_DRIVE"))) {
165                 _max_drive = osd->get_feature_int_value(_T("MAX_DRIVE"));
166         } else if(osd->check_feature(_T("MAX_FD"))) {
167                 _max_drive = osd->get_feature_int_value(_T("MAX_FD"));
168         } else {
169                 _max_drive = 1;
170         }
171         _drive_mask = _max_drive - 1;
172         for(int i = 0; i < _max_drive; i++) {
173                 disk[i] = new DISK(emu);
174                 disk[i]->set_device_name(_T("%s/Disk #%d"), this_device_name, i + 1);
175         }
176         
177         if(osd->check_feature(_T("MB8877_NO_BUSY_AFTER_SEEK"))) {
178                 mb8877_no_busy_after_seek = true;
179         }
180         //if(osd->check_feature(_T("_FDC_DEBUG_LOG"))) {
181         //      fdc_debug_log = true;
182         //}
183         fdc_debug_log = config.special_debug_fdc;
184         
185         if(osd->check_feature(_T("HAS_MB8866"))) {
186                 type_mb8866 = true;
187                 invert_registers = true;
188                 set_device_name(_T("MB8866 FDC"));
189         }
190         if(osd->check_feature(_T("HAS_MB8876"))) {
191                 invert_registers = true;
192                 set_device_name(_T("MB8876 FDC"));
193         } else if(osd->check_feature(_T("HAS_MB89311"))) {
194                 type_mb89311 = true;
195                 set_device_name(_T("MB89311 FDC"));
196         } else {
197                 set_device_name(_T("MB8877 FDC"));
198         }               
199         if(osd->check_feature(_T("_X1")) || osd->check_feature(_T("_X1TWIN")) ||
200                 osd->check_feature(_T("_X1TURBO")) || osd->check_feature(_T("_X1TURBOZ"))) {
201                 type_x1 = true;
202         }
203         if(osd->check_feature(_T("_FM7")) || osd->check_feature(_T("_FM8")) ||
204                 osd->check_feature(_T("_FM77_VARIANTS")) || osd->check_feature(_T("_FM77AV_VARIANTS"))) {
205                 type_fm7 = true;
206                 if(osd->check_feature(_T("_FM77AV40")) || osd->check_feature(_T("_FM77AV40EX")) ||
207                    osd->check_feature(_T("_FM77AV40SX")) ||
208                    osd->check_feature(_T("_FM77AV20")) || osd->check_feature(_T("_FM77AV20EX"))) {
209                         type_fm77av_2dd = true;
210                 }
211         }
212         if(osd->check_feature(_T("_FMR50"))) {
213                 type_fmr50 = true;
214         }
215         if(osd->check_feature(_T("_FMR60"))) {
216                 type_fmr60 = true;
217         }
218         
219         // initialize noise
220         if(d_noise_seek != NULL) {
221                 d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)"));
222                 if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) {
223                         if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) {
224                                 d_noise_seek->load_wav_file(_T("SEEK.WAV"));
225                         }
226                 }
227                 d_noise_seek->set_mute(!config.sound_noise_fdd);
228         }
229         if(d_noise_head_down != NULL) {
230                 d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)"));
231                 d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV"));
232                 d_noise_head_down->set_mute(!config.sound_noise_fdd);
233         }
234         if(d_noise_head_up != NULL) {
235                 d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)"));
236                 d_noise_head_up->load_wav_file(_T("HEADUP.WAV"));
237                 d_noise_head_up->set_mute(!config.sound_noise_fdd);
238         }
239         
240         // initialize timing
241         memset(fdc, 0, sizeof(fdc));
242         for(int i = 0; i < 16; i++) {
243                 fdc[i].track = 40;  // OK?
244                 fdc[i].count_immediate = false;
245         }
246         // initialize fdc
247         seektrk = 0;
248         seekvct = true;
249         status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
250         drvreg = 0;
251         prev_drq_clock = seekend_clock = 0;
252 }
253
254 void MB8877::release()
255 {
256         // release d88 handler
257         for(int i = 0; i < _max_drive; i++) {
258                 if(disk[i]) {
259                         disk[i]->close();
260                         delete disk[i];
261                 }
262         }
263 }
264
265 void MB8877::reset()
266 {
267         for(int i = 0; i < _max_drive; i++) {
268                 //fdc[i].track = 0; // Hold to track
269                 fdc[i].index = 0;
270                 fdc[i].access = false;
271         }
272         for(int i = 0; i < (int)array_length(register_id); i++) {
273                 register_id[i] = -1;
274         }
275         now_search = now_seek = drive_sel = false;
276         no_command = 0;
277         
278 //#ifdef HAS_MB89311
279         if(type_mb89311) {
280                 extended_mode = true;
281         } else {
282                 extended_mode = false;
283         }
284 //#endif
285 }
286
287 void MB8877::write_io8(uint32_t addr, uint32_t data)
288 {
289         bool ready;
290         
291         switch(addr & 3) {
292         case 0:
293                 // command reg
294                 cmdreg_tmp = cmdreg;
295 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
296                 if(invert_registers) {
297                         cmdreg = (~data) & 0xff;
298                 } else {
299 //#else
300                         cmdreg = data;
301                 }
302 //#endif
303                 process_cmd();
304                 no_command = 0;
305                 break;
306         case 1:
307                 // track reg
308 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
309                 if(invert_registers) {
310                         trkreg = (~data) & 0xff;
311                 } else {
312 //#else
313                         trkreg = data;
314                 }
315 //#endif
316                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
317                         // track reg is written after command starts
318                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
319                                 process_cmd();
320                         }
321                 }
322                 break;
323         case 2:
324                 // sector reg
325 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
326                 if(invert_registers) {
327                         secreg = (~data) & 0xff;
328                 } else {
329 //#else
330                         secreg = data;
331                 }
332 //#endif
333                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
334                         // sector reg is written after command starts
335                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
336                                 process_cmd();
337                         }
338                 }
339                 break;
340         case 3:
341                 // data reg
342 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
343                 if(invert_registers) {
344                         datareg = (~data) & 0xff;
345                 } else {
346 //#else
347                         datareg = data;
348                 }
349 //#endif
350                 ready = ((status & FDC_ST_DRQ) && !now_search);
351 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
352                 if(type_fm7) {
353                         if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
354                                 if(!motor_on) ready = false;
355                         }
356                 } else 
357 //#endif
358                 {
359                         if(!motor_on) ready = false;
360                 }
361 //              if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
362                 if(ready) {
363                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
364                                 // write or multisector write
365                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
366                                         if(!disk[drvreg]->write_protected) {
367                                                 if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
368                                                         disk[drvreg]->sector[fdc[drvreg].index] = datareg;
369                                                         sector_changed = true;
370                                                 }
371                                                 // dm, ddm
372                                                 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
373                                         } else {
374                                                 status |= FDC_ST_WRITEFAULT;
375                                                 status &= ~FDC_ST_BUSY;
376                                                 cmdtype = 0;
377                                                 set_irq(true);
378                                         }
379                                         //fdc[drvreg].index++;
380                                 }
381                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
382                                         if(cmdtype == FDC_CMD_WR_SEC) {
383                                                 // single sector
384 //#ifdef _FDC_DEBUG_LOG
385                                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
386 //#endif
387                                                 status &= ~FDC_ST_BUSY;
388                                                 cmdtype = 0;
389                                                 set_irq(true);
390                                         } else {
391                                                 // multisector
392 //#ifdef _FDC_DEBUG_LOG
393                                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
394 //#endif
395                                                 // 2HD: 360rpm, 10410bytes/track -> 0.06246bytes/us
396                                                 register_my_event(EVENT_MULTI1, 30); // 0.06246bytes/us * 30us = 1.8738bytes < GAP3
397                                                 register_my_event(EVENT_MULTI2, 60); // 0.06246bytes/us * 60us = 3.7476bytes < GAP3
398                                         }
399                                         sector_changed = false;
400                                 } else if(status & FDC_ST_DRQ) {
401                                         if(fdc[drvreg].index == 0) {
402                                                 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
403                                         } else {
404                                                 register_drq_event(1);
405                                         }
406                                         if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
407                                 }
408                                 status &= ~FDC_ST_DRQ;
409                         } else if(cmdtype == FDC_CMD_WR_TRK) {
410                                 // write track
411                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
412                                         if(!disk[drvreg]->write_protected) {
413                                                 if(fdc[drvreg].index == 0) {
414                                                         disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
415                                                         fdc[drvreg].id_written = false;
416                                                         fdc[drvreg].side = sidereg;
417                                                         fdc[drvreg].side_changed = false;
418                                                 }
419                                                 if(fdc[drvreg].side != sidereg) {
420                                                         fdc[drvreg].side_changed = true;
421                                                 }
422                                                 if(fdc[drvreg].side_changed) {
423                                                         // abort write track because disk side is changed
424                                                 } else if(datareg == 0xf5) {
425                                                         // write a1h in missing clock
426                                                 } else if(datareg == 0xf6) {
427                                                         // write c2h in missing clock
428                                                 } else if(datareg == 0xf7) {
429                                                         // write crc
430                                                         if(!fdc[drvreg].id_written) {
431                                                                 // insert new sector with data crc error
432 write_id:
433                                                                 uint8_t c = 0, h = 0, r = 0, n = 0;
434                                                                 fdc[drvreg].id_written = true;
435                                                                 fdc[drvreg].sector_found = false;
436                                                                 if(fdc[drvreg].index >= 4) {
437                                                                         c = disk[drvreg]->track[fdc[drvreg].index - 4];
438                                                                         h = disk[drvreg]->track[fdc[drvreg].index - 3];
439                                                                         r = disk[drvreg]->track[fdc[drvreg].index - 2];
440                                                                         n = disk[drvreg]->track[fdc[drvreg].index - 1];
441                                                                 }
442                                                                 fdc[drvreg].sector_length = 0x80 << (n & 3);
443                                                                 fdc[drvreg].sector_index = 0;
444                                                                 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
445                                                         } else if(fdc[drvreg].sector_found) {
446                                                                 // clear data crc error if all sector data are written
447                                                                 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
448                                                                         disk[drvreg]->set_data_crc_error(false);
449                                                                 }
450                                                                 fdc[drvreg].id_written = false;
451                                                         } else {
452                                                                 // data mark of current sector is not written
453                                                                 disk[drvreg]->set_data_mark_missing();
454                                                                 goto write_id;
455                                                         }
456                                                 } else if(fdc[drvreg].id_written) {
457                                                         if(fdc[drvreg].sector_found) {
458                                                                 // sector data
459                                                                 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
460                                                                         disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
461                                                                 }
462                                                                 fdc[drvreg].sector_index++;
463                                                         } else if(datareg == 0xf8 || datareg == 0xfb) {
464                                                                 // data mark
465                                                                 disk[drvreg]->set_deleted(datareg == 0xf8);
466                                                                 fdc[drvreg].sector_found = true;
467                                                         }
468                                                 }
469                                                 disk[drvreg]->track[fdc[drvreg].index] = datareg;
470                                         } else {
471                                                 status |= FDC_ST_WRITEFAULT;
472                                                 status &= ~FDC_ST_BUSY;
473                                                 status &= ~FDC_ST_DRQ;
474                                                 cmdtype = 0;
475                                                 set_irq(true);
476                                         }
477                                         //fdc[drvreg].index++;
478                                 }
479                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
480                                         if(!disk[drvreg]->write_protected) {
481                                                 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
482                                                         // data mark of last sector is not written
483                                                         disk[drvreg]->set_data_mark_missing();
484                                                 }
485                                                 disk[drvreg]->sync_buffer();
486                                         }
487                                         status &= ~FDC_ST_BUSY;
488                                         cmdtype = 0;
489                                         set_irq(true);
490                                 } else if(status & FDC_ST_DRQ) {
491                                         if(fdc[drvreg].index == 0) {
492                                                 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
493                                         } else {
494                                                 register_drq_event(1);
495                                         }
496                                         if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
497                                 }
498                                 status &= ~FDC_ST_DRQ;
499                         }
500                         if(!(status & FDC_ST_DRQ)) {
501                                 cancel_my_event(EVENT_LOST);
502                                 set_drq(false);
503                                 fdc[drvreg].access = true;
504                         }
505                 }
506                 break;
507         }
508 }
509
510 uint32_t MB8877::read_io8(uint32_t addr)
511 {
512         uint32_t val;
513         bool not_ready = false;
514         bool ready;
515         
516         switch(addr & 3) {
517         case 0:
518                 // status reg
519                 if(now_search) {
520                         // now sector search
521                         val = FDC_ST_BUSY;
522                 } else {
523                         // disk not inserted, motor stop
524                         not_ready = !disk[drvreg]->inserted;
525 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
526                         if(type_fm7){
527                                 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
528                                         if(!motor_on) not_ready = true;
529                                 }
530                         } else
531 //#endif
532                         {
533                                 if(!motor_on) not_ready = true;
534                         }
535 //                      if(!disk[drvreg]->inserted || !motor_on) {
536                         if(not_ready) {
537                                 status |= FDC_ST_NOTREADY;
538                         } else {
539                                 status &= ~FDC_ST_NOTREADY;
540                         }
541                         // write protected
542                         if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
543                                 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
544                                         status |= FDC_ST_WRITEP;
545                                 } else {
546                                         status &= ~FDC_ST_WRITEP;
547                                 }
548                         } else {
549                                 status &= ~FDC_ST_WRITEP;
550                         }
551                         // track0, index hole
552                         if(cmdtype == FDC_CMD_TYPE1) {
553                                 if(fdc[drvreg].track == 0) {
554                                         status |= FDC_ST_TRACK00;
555                                 } else {
556                                         status &= ~FDC_ST_TRACK00;
557                                 }
558                                 // index hole signal width is 5msec (thanks Mr.Sato)
559                                 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
560                                         status |= FDC_ST_INDEX;
561                                 } else {
562                                         status &= ~FDC_ST_INDEX;
563                                 }
564                         }
565                         // show busy a moment
566                         val = status;
567                         if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
568                                 status &= ~FDC_ST_BUSY;
569 //#ifdef MB8877_NO_BUSY_AFTER_SEEK
570                                 if(mb8877_no_busy_after_seek) {
571                                         //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
572                                         if(type_fm7) {
573                                                 if(disk[0]->is_special_disk != SPECIAL_DISK_FM7_XANADU2_D) {
574                                                         val &= ~FDC_ST_BUSY;
575                                                 }
576                                         } else
577                                                 //#endif
578                                         {
579                                                 val &= ~FDC_ST_BUSY;
580                                         }
581                                 }
582 //#endif
583                         }
584                 }
585
586                 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
587                         // MZ-2000 HuBASIC invites NOT READY status
588                         if(++no_command == 16) {
589                                 val |= FDC_ST_NOTREADY;
590                         }
591                 } else {
592                         no_command = 0;
593                 }
594
595                 // reset irq/drq
596                 if(!(status & FDC_ST_DRQ)) {
597                         set_drq(false);
598                 }
599                 if(!(status & FDC_ST_BUSY)) {
600                         set_irq(false);
601                 }
602 //#ifdef _FDC_DEBUG_LOG
603                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
604 //#endif
605 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
606                         if(invert_registers) {
607                                 return (~val) & 0xff;
608 //#else
609                         } else {
610                                 return val;
611                         }
612 //#endif
613         case 1:
614                 // track reg
615 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
616                         if(invert_registers) {
617                                 return (~trkreg) & 0xff;
618                         } else {
619 //#else
620                                 return trkreg;
621                         }
622 //#endif
623         case 2:
624                 // sector reg
625 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
626                         if(invert_registers) {
627                                 return (~secreg) & 0xff;
628                         } else {
629 //#else
630                                 return secreg;
631                         }
632 //#endif
633         case 3:
634                 // data reg
635                 ready = ((status & FDC_ST_DRQ) && !now_search);
636 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
637                 if(type_fm7) {
638                         if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
639                                 if(!motor_on) ready = false;
640                         }
641                 } else
642 //#endif
643                 {
644                         if(!motor_on) ready = false;
645                 }
646 //              if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
647                 if(ready) {
648                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
649                                 // read or multisector read
650                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
651                                         datareg = disk[drvreg]->sector[fdc[drvreg].index];
652                                 }
653                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {  // Reading complete this sector.
654
655                                         if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
656                                                 // data crc error
657 #if 0
658 //#ifdef _FDC_DEBUG_LOG
659                                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
660 //#endif
661                                                 status |= FDC_ST_CRCERR;
662                                                 status &= ~FDC_ST_BUSY;
663                                                 cmdtype = 0;
664                                                 set_irq(true);
665 #else
666                                                 register_my_event_with_check(EVENT_CRC_ERROR, disk[drvreg]->get_usec_per_bytes(3));
667 #endif
668                                         } else if(cmdtype == FDC_CMD_RD_SEC) {
669                                                 // single sector
670 #if 0
671 //#ifdef _FDC_DEBUG_LOG
672                                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
673 //#endif
674                                                 status &= ~FDC_ST_BUSY;
675                                                 cmdtype = 0;
676                                                 set_irq(true);
677 #else
678                                                 register_my_event_with_check(EVENT_END_READ_SECTOR, disk[drvreg]->get_usec_per_bytes(3));
679 #endif
680                                         } else {
681                                                 // multisector
682 //#ifdef _FDC_DEBUG_LOG
683                                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
684 //#endif
685                                                 register_my_event(EVENT_MULTI1, 30);
686                                                 register_my_event(EVENT_MULTI2, 60);
687                                         }
688                                 } else { // Still data remain.
689                                         register_drq_event(1);
690                                         if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
691                                 }
692                                 status &= ~FDC_ST_DRQ;
693                         } else if(cmdtype == FDC_CMD_RD_ADDR) {
694                                 // read address
695                                 if(fdc[drvreg].index < 6) {
696                                         datareg = disk[drvreg]->id[fdc[drvreg].index];
697                                         //fdc[drvreg].index++;
698                                 }
699                                 if((fdc[drvreg].index + 1) >= 6) {
700                                         if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
701                                                 // id crc error
702                                                 status |= FDC_ST_CRCERR;
703                                         }
704                                         status &= ~FDC_ST_BUSY;
705                                         cmdtype = 0;
706                                         set_irq(true);
707 //#ifdef _FDC_DEBUG_LOG
708                                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
709 //#endif
710                                 } else {
711                                         register_drq_event(1);
712                                         if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
713                                 }
714                                 status &= ~FDC_ST_DRQ;
715                         } else if(cmdtype == FDC_CMD_RD_TRK) {
716                                 // read track
717                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
718                                         datareg = disk[drvreg]->track[fdc[drvreg].index];
719                                         //fdc[drvreg].index++;
720                                 }
721                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
722 //#ifdef _FDC_DEBUG_LOG
723                                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF TRACK\n"));
724 ///#endif
725                                         status &= ~FDC_ST_BUSY;
726                                         status |= FDC_ST_LOSTDATA;
727                                         cmdtype = 0;
728                                         set_irq(true);
729                                 } else {
730                                         register_drq_event(1);
731                                         if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
732                                 }
733                                 status &= ~FDC_ST_DRQ;
734                         }
735                         if(!(status & FDC_ST_DRQ)) {
736                                 cancel_my_event(EVENT_LOST);
737                                 set_drq(false);
738                                 fdc[drvreg].access = true;
739                         }
740                 }
741 //#ifdef _FDC_DEBUG_LOG
742                 if(fdc_debug_log) this->force_out_debug_log(_T("FDC\tDATA=%2x\n"), datareg); // emu->force_out_debug_log()
743 //#endif
744 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
745                 if(invert_registers) {
746                         return (~datareg) & 0xff;
747                 } else {
748 //#else
749                         return datareg;
750                 }
751 //#endif
752         }
753         return 0xff;
754 }
755
756 void MB8877::write_dma_io8(uint32_t addr, uint32_t data)
757 {
758         write_io8(3, data);
759 }
760
761 uint32_t MB8877::read_dma_io8(uint32_t addr)
762 {
763         return read_io8(3);
764 }
765
766 void MB8877::write_signal(int id, uint32_t data, uint32_t mask)
767 {
768         if(id == SIG_MB8877_DRIVEREG) {
769                 drvreg = data & _drive_mask;
770                 drive_sel = true;
771                 seekend_clock = get_current_clock();
772         } else if(id == SIG_MB8877_SIDEREG) {
773                 sidereg = (data & mask) ? 1 : 0;
774         } else if(id == SIG_MB8877_MOTOR) {
775                 motor_on = ((data & mask) != 0);
776         } 
777 }
778
779 uint32_t MB8877::read_signal(int ch)
780 {
781         if(ch == SIG_MB8877_DRIVEREG) {
782                 return drvreg & _drive_mask;
783         } else if(ch == SIG_MB8877_SIDEREG) {
784                 return sidereg & 1;
785         } else if(ch == SIG_MB8877_MOTOR) {
786                 return motor_on ? 1 : 0;
787         }
788         
789         // get access status
790         uint32_t stat = 0;
791         for(int i = 0; i < _max_drive; i++) {
792                 if(fdc[i].access) {
793                         stat |= 1 << i;
794                 }
795                 fdc[i].access = false;
796         }
797         if(now_search) {
798                 stat |= 1 << drvreg;
799         }
800         return stat;
801 }
802
803 void MB8877::event_callback(int event_id, int err)
804 {
805         int event = event_id >> 8;
806         int cmd = event_id & 0xff;
807         //bool need_after_irq = false;
808         register_id[event] = -1;
809         
810         // cancel event if the command is finished or other command is executed
811         if(cmd != cmdtype) {
812                 if(event == EVENT_SEEK || event == EVENT_SEEKEND_VERIFY) {
813                         now_seek = false;
814                 } else if(event == EVENT_SEARCH) {
815                         now_search = false;
816                 }
817                 return;
818         }
819         
820         switch(event) {
821         case EVENT_SEEK:
822 //#ifdef _FDC_DEBUG_LOG
823                 //this->out_debug_log(_T("FDC\tSEEK START\n"));
824 //#endif
825                 if(seektrk > fdc[drvreg].track) {
826                         fdc[drvreg].track++;
827                         if(d_noise_seek != NULL) d_noise_seek->play();
828                         //need_after_irq = true;
829                 } else if(seektrk < fdc[drvreg].track) {
830                         fdc[drvreg].track--;
831                         if(d_noise_seek != NULL) d_noise_seek->play();
832                         //need_after_irq = true;
833                 }
834                 // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
835                 if((((cmdreg & 0x10) != 0) && (cmdreg >= 0x20)) || ((cmdreg & 0xf0) == 0)) {
836                         trkreg = fdc[drvreg].track;
837                 }
838                 if((uint8_t)seektrk != (uint8_t)fdc[drvreg].track) {
839                         //if(need_after_irq) {
840                                 //set_drq(false);
841                                 //set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
842                         //}
843                         register_seek_event();
844                         break;
845                 }
846 #if 1
847                 // Verify before execute command.
848                 // Port from XM7.Thanks to Ryu Takegami.
849                 if(cmdreg & 4) {
850                 // verify
851                         if(trkreg != fdc[drvreg].track) {
852                                 status |= FDC_ST_SEEKERR;
853                                 trkreg = fdc[drvreg].track; // Reload track register when verify: Really OK? 20180224 K.O
854                         }
855                 }
856 #endif
857                 seekend_clock = get_current_clock();
858 //#ifdef HAS_MB89311
859                 if(type_mb89311) {
860                         if(extended_mode) {
861                                 if((cmdreg & 0xf4) == 0x44) {
862                                         // read-after-seek
863                                         cmd_readdata(true);
864                                         break;
865                                 } else if((cmdreg & 0xf4) == 0x64) {
866                                         // write-after-seek
867                                         cmd_writedata(true);
868                                         break;
869                                 }
870                         }
871                 }
872 //#endif
873                 status_tmp = status;
874                 if(cmdreg & 4) {
875                         // verify
876                         status_tmp |= search_track();
877                         double time;
878 #if 0                   
879                         if(status_tmp & FDC_ST_SEEKERR) {
880                                 time = get_usec_to_detect_index_hole(5, true);
881                         } else {
882                                 time = get_usec_to_next_trans_pos(true);
883                         }
884 #else
885                         // Verify wait is shorter than above at least FM-7 series's MB8877. 20180223 K.O.
886                         // Thanks to Ryu Takegami.
887                         time = (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ? 10000.0 : 20000.0;
888 #endif
889                         // 20180128 K.O If seek completed, delay to make IRQ/DRQ at SEEKEND_VERIFY.
890                         register_my_event(EVENT_SEEKEND_VERIFY, time);
891                         break;
892                 }
893 //      case EVENT_SEEKEND: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
894                 now_seek = false;
895                 status = status_tmp;
896                 status &= (uint8_t)(~FDC_ST_BUSY);      // 20180118 K.O Turn off BUSY flag.
897                 set_drq(false);
898                 set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
899                 break;
900         case EVENT_SEEKEND_VERIFY: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
901                 now_seek = false;
902                 status &= (uint8_t)(~FDC_ST_BUSY);      // 20180118 K.O Turn off BUSY flag.
903                 set_drq(false);
904                 set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
905 //#ifdef _FDC_DEBUG_LOG
906                 //this->out_debug_log(_T("FDC\tSEEK END\n"));
907 //#endif
908                 break;
909         case EVENT_SEARCH:
910                 now_search = false;
911                 if(status_tmp & FDC_ST_RECNFND) {
912 //#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
913                         if(type_x1) {
914                                 // for SHARP X1 Batten Tanuki
915                                 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
916                                         status_tmp &= ~FDC_ST_RECNFND;
917                                 }
918                         }
919 //#endif
920 //#ifdef _FDC_DEBUG_LOG
921                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH NG\n"));
922 //#endif
923                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
924                         cmdtype = 0;
925                         set_drq(false);
926                         set_irq(true);
927                 } else if(status_tmp & FDC_ST_WRITEFAULT) {
928 //#ifdef _FDC_DEBUG_LOG
929                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
930 //#endif
931                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
932                         cmdtype = 0;
933                         set_drq(false);
934                         set_irq(true);
935                 } else {
936                         status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
937                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
938                                 register_lost_event(8);
939                         } else if(cmdtype == FDC_CMD_WR_TRK) {
940                                 register_lost_event(3);
941                         } else {
942                                 register_lost_event(1);
943                         }
944                         fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
945                         fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
946                         set_drq(true);
947                         drive_sel = false;
948                         if(fdc_debug_log) this->out_debug_log("DRQ ON@SEARCH: %d\n", prev_drq_clock);
949 //#ifdef _FDC_DEBUG_LOG
950                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH OK\n"));
951 //#endif
952                 }
953                 break;
954         case EVENT_DRQ:
955                 if(status & FDC_ST_BUSY) {
956                         status |= FDC_ST_DRQ;
957                         register_lost_event(1);
958                         if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
959                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
960                         } else {
961                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
962                         }
963                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
964                            cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
965                            cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK  ||
966                            cmdtype == FDC_CMD_RD_ADDR) {
967                                 if(!(fdc[drvreg].count_immediate)) fdc[drvreg].index++;
968                         }
969                         fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
970
971                         set_drq(true);
972                         if(fdc_debug_log) this->out_debug_log("DRQ ON@DRQ: %d\n", prev_drq_clock);
973 //#ifdef _FDC_DEBUG_LOG
974                         //this->out_debug_log(_T("FDC\tDRQ!\n"));
975 //#endif
976                 }
977                 break;
978         case EVENT_MULTI1:
979                 secreg++;
980                 break;
981         case EVENT_MULTI2:
982                 if(cmdtype == FDC_CMD_RD_MSEC) {
983                         cmd_readdata(false);
984                 } else if(cmdtype == FDC_CMD_WR_MSEC) {
985                         cmd_writedata(false);
986                 }
987                 break;
988         case EVENT_CRC_ERROR:
989                 //#ifdef _FDC_DEBUG_LOG
990                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
991 //#endif
992                 status |= FDC_ST_CRCERR;
993                 status &= ~FDC_ST_BUSY;
994                 cmdtype = 0;
995                 set_irq(true);
996                 break;
997         case EVENT_END_READ_SECTOR:
998 //#ifdef _FDC_DEBUG_LOG
999                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
1000 //#endif
1001                 status &= ~FDC_ST_BUSY;
1002                 cmdtype = 0;
1003                 set_irq(true);
1004                 break;
1005         case EVENT_LOST:
1006                 if(status & FDC_ST_BUSY) {
1007 //#ifdef _FDC_DEBUG_LOG
1008                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tDATA LOST\n"));
1009 //#endif
1010                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
1011                                 if(fdc[drvreg].index == 0) { // HEAD of REGION
1012                                         cmdtype = 0;
1013                                         //if((status & FDC_ST_DRQ) != 0) { // 20180130 FORCE DOWN DRQ ON LOST-DATA.
1014                                                 status |= FDC_ST_LOSTDATA;
1015                                                 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1016                                                 set_drq(false);
1017                                                 //}
1018                                                 set_irq(true);
1019                                 } else { // STILL WRITING
1020                                         write_io8(3, 0x00);
1021                                         //if((status & FDC_ST_DRQ) != 0) {
1022                                                 status |= FDC_ST_LOSTDATA;
1023                                                 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1024                                                 set_irq(true);
1025                                                 set_drq(false);
1026                                                 //cmdtype = 0;
1027                                                 //}
1028                                 }
1029                         } else { // READ 
1030                                 //if((status & FDC_ST_DRQ) != 0) {
1031                                         status |= FDC_ST_LOSTDATA;
1032                                         status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1033                                         set_irq(true);
1034                                         set_drq(false);
1035                                         //}
1036                                 read_io8(3);
1037                         }
1038                         status |= FDC_ST_LOSTDATA;
1039                 }
1040                 break;
1041         }
1042 }
1043
1044 // ----------------------------------------------------------------------------
1045 // command
1046 // ----------------------------------------------------------------------------
1047
1048 void MB8877::process_cmd()
1049 {
1050         set_irq(false);
1051         set_drq(false);
1052         
1053 //#ifdef HAS_MB89311
1054         // MB89311 mode commands
1055         if(type_mb89311) {
1056                                 if(cmdreg == 0xfc) {
1057                                 // delay (may be only in extended mode)
1058 //#ifdef _FDC_DEBUG_LOG
1059                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (DELAY   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1060 //#endif
1061                                 cmdtype = status = 0;
1062                                 return;
1063                         } else if(cmdreg == 0xfd) {
1064                                 // assign parameter
1065 //#ifdef _FDC_DEBUG_LOG
1066                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN PAR) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1067 //#endif
1068                                 cmdtype = status = 0;
1069                                 return;
1070                         } else if(cmdreg == 0xfe) {
1071                                 // assign mode
1072 //#ifdef _FDC_DEBUG_LOG
1073                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN MOD) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1074 //#endif
1075                                 extended_mode = !extended_mode;
1076                                 cmdtype = status = 0;
1077                                 return;
1078                         } else if(cmdreg == 0xff) {
1079                                 // reset (may be only in extended mode)
1080 //#ifdef _FDC_DEBUG_LOG
1081                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (RESET   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1082 //#endif
1083                                 cmd_forceint();
1084                                 extended_mode = true;
1085                                 return;
1086                         } else if(extended_mode) {
1087                                 // type-1
1088                                 if((cmdreg & 0xeb) == 0x21) {
1089 //#ifdef _FDC_DEBUG_LOG
1090                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (STEP IN ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1091 //#endif
1092                                 cmd_stepin();
1093                                 return;
1094                         } else if((cmdreg & 0xeb) == 0x22) {
1095 //#ifdef _FDC_DEBUG_LOG
1096                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (STEP OUT) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1097 //#endif
1098                                 cmd_stepout();
1099                                 return;
1100                                 // type-2
1101                         } else if((cmdreg & 0xf4) == 0x44) {
1102                                 // read-after-seek
1103 //#ifdef _FDC_DEBUG_LOG
1104                                         if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (RDaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1105 //#endif
1106                                         cmd_seek();
1107                                         return;
1108                                 } else if((cmdreg & 0xf4) == 0x64) {
1109                                         // write-after-seek
1110 //#ifdef _FDC_DEBUG_LOG
1111                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (WRaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1112 //#endif
1113                                 cmd_seek();
1114                         return;
1115                         // type-3
1116                         } else if((cmdreg & 0xfb) == 0xf1) {
1117                                         // format
1118 //#ifdef _FDC_DEBUG_LOG
1119                                 if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (FORMAT  ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
1120 //#endif
1121                                 cmd_format();
1122                                 return;
1123                                 }
1124                                 }
1125         }
1126 //#endif
1127         
1128         // MB8877 mode commands
1129 //#ifdef _FDC_DEBUG_LOG
1130         static const _TCHAR *cmdstr[0x10] = {
1131                 _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),
1132                 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
1133                 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
1134                 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
1135         };
1136         if(fdc_debug_log) this->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);
1137 //#endif
1138         switch(cmdreg & 0xf8) {
1139         // type-1
1140         case 0x00: case 0x08:
1141                 cmd_restore();
1142                 update_head_flag(drvreg, (cmdreg & 8) != 0);
1143                 break;
1144         case 0x10: case 0x18:
1145                 cmd_seek();
1146                 update_head_flag(drvreg, (cmdreg & 8) != 0);
1147                 break;
1148         case 0x20: case 0x28:
1149         case 0x30: case 0x38:
1150                 cmd_step();
1151                 update_head_flag(drvreg, (cmdreg & 8) != 0);
1152                 break;
1153         case 0x40: case 0x48:
1154         case 0x50: case 0x58:
1155                 cmd_stepin();
1156                 update_head_flag(drvreg, (cmdreg & 8) != 0);
1157                 break;
1158         case 0x60: case 0x68:
1159         case 0x70: case 0x78:
1160                 cmd_stepout();
1161                 update_head_flag(drvreg, (cmdreg & 8) != 0);
1162                 break;
1163         // type-2
1164         case 0x80: case 0x88:
1165         case 0x90: case 0x98:
1166                 cmd_readdata(true);
1167                 update_head_flag(drvreg, true);
1168                 break;
1169         case 0xa0:case 0xa8:
1170         case 0xb0: case 0xb8:
1171                 cmd_writedata(true);
1172                 update_head_flag(drvreg, true);
1173                 break;
1174         // type-3
1175         case 0xc0:
1176                 cmd_readaddr();
1177                 update_head_flag(drvreg, true);
1178                 break;
1179         case 0xe0:
1180                 cmd_readtrack();
1181                 update_head_flag(drvreg, true);
1182                 break;
1183         case 0xf0:
1184                 cmd_writetrack();
1185                 update_head_flag(drvreg, true);
1186                 break;
1187         // type-4
1188         case 0xd0: case 0xd8:
1189                 cmd_forceint();
1190                 break;
1191         // unknown command
1192         default:
1193                 break;
1194         }
1195 }
1196
1197 void MB8877::cmd_restore()
1198 {
1199         // type-1 restore
1200         cmdtype = FDC_CMD_TYPE1;
1201         if(!check_drive()) {
1202                 return;
1203         }
1204         if((cmdreg & 0x08) != 0) { // Head engage
1205                 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1206         } else {
1207                 status = FDC_ST_BUSY;
1208         }
1209         set_irq(false);
1210         set_drq(false);
1211
1212         if(fdc[drvreg].track < 0) {
1213                 fdc[drvreg].track = 0;
1214                 trkreg = 0;
1215         } else if(fdc[drvreg].track >= 80) {
1216                 fdc[drvreg].track = 80;
1217                 trkreg = 80;
1218         } else {
1219                 trkreg = fdc[drvreg].track;
1220         }
1221         datareg = 0;
1222         
1223         seektrk = 0;
1224         seekvct = true;
1225         
1226         register_seek_event();
1227 }
1228
1229 void MB8877::cmd_seek()
1230 {
1231         // type-1 seek
1232         cmdtype = FDC_CMD_TYPE1;
1233         if(!check_drive()) {
1234                 return;
1235         }
1236         if((cmdreg & 0x08) != 0) { // Head engage
1237                 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1238         } else {
1239                 status = FDC_ST_BUSY;
1240         }               
1241
1242         // revert below 20180225 K.O
1243         //seektrk = (uint8_t)(fdc[drvreg].track + datareg - trkreg); // Seek target is differ when drive's track != trkreg.Thanks to Haserin and Ryu Takegami.
1244         seektrk = (int)((int8_t)datareg);
1245 //#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
1246         if(seektrk >= disk[drvreg]->get_max_tracks()) {
1247                 seektrk = disk[drvreg]->get_max_tracks() - 1;
1248         } else if(seektrk < 0) {
1249                 seektrk = 0;
1250         }
1251
1252         seekvct = !(seektrk > fdc[drvreg].track);
1253         // Update track register by data register.Thanks to Ryu Takegami. 20180224 K.O
1254         trkreg = datareg;
1255         set_irq(false);
1256         set_drq(false);
1257         register_seek_event();
1258 }
1259
1260 void MB8877::cmd_step()
1261 {
1262         // type-1 step
1263         if(seekvct) {
1264                 cmd_stepout();
1265         } else {
1266                 cmd_stepin();
1267         }
1268 }
1269
1270 void MB8877::cmd_stepin()
1271 {
1272         // type-1 step in
1273         cmdtype = FDC_CMD_TYPE1;
1274         if(!check_drive()) {
1275                 return;
1276         }
1277         if((cmdreg & 0x08) != 0) { // Head engage
1278                 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1279         } else {
1280                 status = FDC_ST_BUSY;
1281         }
1282
1283         // Verify before execute command.
1284         // Port from XM7.Thanks to Ryu Takegami.
1285 #if 1
1286         if(cmdreg & 4) {
1287                 // verify
1288                 if(trkreg != fdc[drvreg].track) {
1289                         status |= FDC_ST_SEEKERR;
1290 //                      trkreg = fdc[drvreg].track;
1291                 }
1292         }
1293 #endif
1294         seektrk = fdc[drvreg].track + 1;
1295         if(seektrk >= disk[drvreg]->get_max_tracks()) {
1296                 seektrk = disk[drvreg]->get_max_tracks() - 1;
1297         } else if(seektrk < 0) {
1298                 seektrk = 0;
1299         }
1300         seekvct = false;
1301         set_irq(false);
1302         set_drq(false);
1303         register_seek_event();
1304 }
1305
1306 void MB8877::cmd_stepout()
1307 {
1308         // type-1 step out
1309         cmdtype = FDC_CMD_TYPE1;
1310         if(!check_drive()) {
1311                 return;
1312         }
1313         if((cmdreg & 0x08) != 0) { // Head engage
1314                 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1315         } else {
1316                 status = FDC_ST_BUSY;
1317         }
1318
1319         // Verify before execute command.
1320         // Port from XM7.Thanks to Ryu Takegami.
1321 #if 1
1322         if(cmdreg & 4) {
1323                 // verify
1324                 if(trkreg != fdc[drvreg].track) {
1325                         status |= FDC_ST_SEEKERR;
1326 //                      trkreg = fdc[drvreg].track;
1327                 }
1328         }
1329 #endif
1330         seektrk = fdc[drvreg].track - 1;
1331         if(seektrk >= disk[drvreg]->get_max_tracks()) {
1332                 seektrk = disk[drvreg]->get_max_tracks() - 1;
1333         } else if(seektrk < 0) {
1334                 seektrk = 0;
1335         }
1336         seekvct = true;
1337         register_seek_event();
1338 }
1339
1340 void MB8877::cmd_readdata(bool first_sector)
1341 {
1342         // type-2 read data
1343         cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
1344         if(!check_drive2()) {
1345                 return;
1346         }
1347         status = FDC_ST_BUSY;
1348         status_tmp = search_sector();
1349         now_search = true;
1350         
1351         double time;
1352         if(status_tmp & FDC_ST_RECNFND) {
1353                 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1354         } else {
1355                 time = get_usec_to_start_trans(first_sector);
1356         }
1357         register_my_event(EVENT_SEARCH, time);
1358         cancel_my_event(EVENT_LOST);
1359 }
1360
1361 void MB8877::cmd_writedata(bool first_sector)
1362 {
1363         // type-2 write data
1364         cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
1365         if(!check_drive2()) {
1366                 return;
1367         }
1368         status = FDC_ST_BUSY;
1369         status_tmp = search_sector() & ~FDC_ST_RECTYPE;
1370         now_search = true;
1371         sector_changed = false;
1372         
1373         double time;
1374         if(status_tmp & FDC_ST_RECNFND) {
1375                 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1376         } else if(status & FDC_ST_WRITEFAULT) {
1377                 time = (cmdreg & 4) ? DELAY_TIME : 1;
1378         } else {
1379                 time = get_usec_to_start_trans(first_sector);
1380         }
1381         register_my_event(EVENT_SEARCH, time);
1382         cancel_my_event(EVENT_LOST);
1383 }
1384
1385 void MB8877::cmd_readaddr()
1386 {
1387         // type-3 read address
1388         cmdtype = FDC_CMD_RD_ADDR;
1389         if(!check_drive2()) {
1390                 return;
1391         }
1392         status = FDC_ST_BUSY;
1393         status_tmp = search_addr();
1394         now_search = true;
1395         
1396         double time;
1397         if(status_tmp & FDC_ST_RECNFND) {
1398                 time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
1399         } else {
1400                 time = get_usec_to_start_trans(true);
1401         }
1402         register_my_event(EVENT_SEARCH, time);
1403         cancel_my_event(EVENT_LOST);
1404 }
1405
1406 void MB8877::cmd_readtrack()
1407 {
1408         // type-3 read track
1409         cmdtype = FDC_CMD_RD_TRK;
1410         if(!check_drive2()) {
1411                 return;
1412         }
1413         status = FDC_ST_BUSY;
1414         status_tmp = 0;
1415         
1416         disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1417         fdc[drvreg].index = 0;
1418         now_search = true;
1419         
1420         fdc[drvreg].next_trans_position = 1;
1421         double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
1422         register_my_event(EVENT_SEARCH, time);
1423         cancel_my_event(EVENT_LOST);
1424 }
1425
1426 void MB8877::cmd_writetrack()
1427 {
1428         // type-3 write track
1429         cmdtype = FDC_CMD_WR_TRK;
1430         if(!check_drive2()) {
1431                 return;
1432         }
1433         status = FDC_ST_BUSY;
1434         status_tmp = 0;
1435         
1436         fdc[drvreg].index = 0;
1437         fdc[drvreg].id_written = false;
1438         now_search = true;
1439         
1440         double time;
1441         if(disk[drvreg]->write_protected) {
1442                 status_tmp = FDC_ST_WRITEFAULT;
1443                 time = (cmdreg & 4) ? DELAY_TIME : 1;
1444         } else {
1445                 if(cmdreg & 4) {
1446                         // wait 15msec before raise first drq
1447                         fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1448                         time = DELAY_TIME;
1449                 } else {
1450                         // raise first drq soon
1451                         fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1452                         time = disk[drvreg]->get_usec_per_bytes(1);
1453                 }
1454                 // wait at least 3bytes before check index hole and raise second drq
1455                 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1456                 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1457                         fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1458                 }
1459         }
1460         register_my_event(EVENT_SEARCH, time);
1461         cancel_my_event(EVENT_LOST);
1462 }
1463
1464 //#ifdef HAS_MB89311
1465 void MB8877::cmd_format()
1466 {
1467         if(type_mb89311) {
1468                 // type-3 format (FIXME: need to implement)
1469                 cmdtype = FDC_CMD_WR_TRK;
1470                 status = FDC_ST_BUSY;
1471                 status_tmp = 0;
1472                 
1473                 fdc[drvreg].index = 0;
1474                 fdc[drvreg].id_written = false;
1475                 now_search = true;
1476                 
1477                 status_tmp = FDC_ST_WRITEFAULT;
1478                 double time = (cmdreg & 4) ? DELAY_TIME : 1;
1479                 
1480                 register_my_event(EVENT_SEARCH, time);
1481                 cancel_my_event(EVENT_LOST);
1482         }
1483 }
1484 //#endif
1485
1486 void MB8877::cmd_forceint()
1487 {
1488         // type-4 force interrupt
1489         bool ready = (cmdtype == 0);
1490         bool is_busy = ((status & FDC_ST_BUSY) != 0);
1491         bool now_seek_bak = now_seek;
1492         bool is_type1 = (cmdtype == FDC_CMD_TYPE1);
1493         bool is_read = ((cmdtype == FDC_CMD_RD_SEC) || (cmdtype == FDC_CMD_RD_MSEC) || \
1494                                   (cmdtype == FDC_CMD_RD_ADDR) || (cmdtype == FDC_CMD_RD_TRK));
1495         bool is_trkwrite = (cmdtype == FDC_CMD_WR_TRK);
1496         
1497         if(cmdtype == FDC_CMD_TYPE1) {
1498                 // abort restore/seek/step command
1499                 if(now_seek) {
1500                         if(seektrk > fdc[drvreg].track) {
1501                                 if((fdc[drvreg].track < 0xff) && (fdc[drvreg].track >= 0)) fdc[drvreg].track++;
1502                         } else if(seektrk < fdc[drvreg].track) {
1503                                 if((fdc[drvreg].track <= 0xff) && (fdc[drvreg].track > 0)) fdc[drvreg].track--;
1504                         }
1505                         // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
1506                         if((((cmdreg_tmp & 0x10) != 0) && (cmdreg_tmp >= 0x20)) || ((cmdreg_tmp & 0xf0) == 0)) {
1507                                 trkreg = fdc[drvreg].track;
1508                         }
1509                 }
1510         } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1511                 // abort write sector command
1512                 if(sector_changed) {
1513                         disk[drvreg]->set_data_crc_error(false);
1514                 }
1515         } else if(cmdtype == FDC_CMD_WR_TRK) {
1516                 // abort write track command
1517                 if(!disk[drvreg]->write_protected) {
1518                         if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1519                                 // data mark of last sector is not written
1520                                 disk[drvreg]->set_data_mark_missing();
1521                         }
1522                         disk[drvreg]->sync_buffer();
1523                 }
1524         }
1525         now_search = now_seek = sector_changed = false;
1526         
1527 //#ifdef HAS_MB89311
1528         if((cmdreg == 0xff) && (type_mb89311)) {
1529                 // reset command
1530                 cmdtype = FDC_CMD_TYPE1;
1531                 status = FDC_ST_HEADENG;
1532         } else {
1533 //#endif
1534                 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1535                         cmdtype = FDC_CMD_TYPE1;
1536                     if(!type_fm7) status = FDC_ST_HEADENG; // Hack for FUKUALL.d77 .
1537                 }
1538                 status &= ~FDC_ST_BUSY;
1539                 if((now_seek_bak) || (ready)) {// Refer from XM7, is this write implement? 20180210 K.O
1540                         status = 0;
1541                         if(((ready) || !(is_busy)) && (!type_fm7)) status = FDC_ST_HEADENG;
1542                         // indexcount = 0;
1543                         if(check_drive2()) {
1544                                 if(!(is_read) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEP;
1545                                 if((is_trkwrite) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEFAULT;
1546                                 if(is_type1) {
1547                                         if(fdc[drvreg].track == 0) status |= FDC_ST_TRACK00;
1548                                         if(get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
1549                                                 status |= FDC_ST_INDEX;
1550                                         }
1551                                 }
1552                         }
1553                 }
1554                 // force interrupt if bit0-bit3 is high
1555                 if(cmdreg & 0x0f) {
1556                         set_irq(true);
1557                 }
1558 //#ifdef HAS_MB89311
1559         }
1560 //#endif
1561         
1562         cancel_my_event(EVENT_SEEK);
1563         cancel_my_event(EVENT_SEEKEND_VERIFY);
1564         cancel_my_event(EVENT_SEARCH);
1565         cancel_my_event(EVENT_DRQ);
1566         cancel_my_event(EVENT_MULTI1);
1567         cancel_my_event(EVENT_MULTI2);
1568         cancel_my_event(EVENT_LOST);
1569 }
1570
1571 void MB8877::update_head_flag(int drv, bool head_load)
1572 {
1573         if(fdc[drv].head_load != head_load) {
1574                 if(head_load) {
1575                         if(d_noise_head_down != NULL) d_noise_head_down->play();
1576                 } else {
1577                         if(d_noise_head_up != NULL) d_noise_head_up->play();
1578                 }
1579                 fdc[drv].head_load = head_load;
1580         }
1581 }
1582
1583 // ----------------------------------------------------------------------------
1584 // media handler
1585 // ----------------------------------------------------------------------------
1586
1587 uint8_t MB8877::search_track()
1588 {
1589         // get track
1590         int track = fdc[drvreg].track;
1591
1592         if(!disk[drvreg]->get_track(track, sidereg)){
1593                 return FDC_ST_SEEKERR;
1594         }
1595         
1596         // verify track number
1597         if(disk[drvreg]->ignore_crc()) {
1598                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1599                         disk[drvreg]->get_sector(-1, -1, i);
1600                         if(disk[drvreg]->id[0] == trkreg) {
1601                                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1602                                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1603                                 return 0;
1604                         }
1605                 }
1606         } else {
1607                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1608                         disk[drvreg]->get_sector(-1, -1, i);
1609                         if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1610                                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1611                                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1612                                 return 0;
1613                         }
1614                 }
1615                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1616                         disk[drvreg]->get_sector(-1, -1, i);
1617                         if(disk[drvreg]->id[0] == trkreg) {
1618                                 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1619                         }
1620                 }
1621         }
1622         return FDC_ST_SEEKERR;
1623 }
1624
1625 uint8_t MB8877::search_sector()
1626 {
1627         // write protect
1628         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1629                 if(disk[drvreg]->write_protected) {
1630                         return FDC_ST_WRITEFAULT;
1631                 }
1632         }
1633         
1634         // get track
1635         int track = fdc[drvreg].track;
1636
1637         
1638         if(!disk[drvreg]->get_track(track, sidereg)) {
1639                 return FDC_ST_RECNFND;
1640         }
1641         
1642         // get current position
1643         int sector_num = disk[drvreg]->sector_num.sd;
1644         int position = get_cur_position();
1645         
1646         if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1647                 position -= disk[drvreg]->get_track_size();
1648         }
1649         
1650         // first scanned sector
1651         int first_sector = 0;
1652         for(int i = 0; i < sector_num; i++) {
1653                 if(position < disk[drvreg]->am1_position[i]) {
1654                         first_sector = i;
1655                         break;
1656                 }
1657         }
1658         
1659         // scan sectors
1660         for(int i = 0; i < sector_num; i++) {
1661                 // get sector
1662                 int index = (first_sector + i) % sector_num;
1663                 disk[drvreg]->get_sector(-1, -1, index);
1664                 
1665                 // check id
1666                 if(disk[drvreg]->id[0] != trkreg) {
1667                         continue;
1668                 }
1669 //#if !defined(HAS_MB8866)
1670                 if(!type_mb8866) {
1671                         if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1672                                 continue;
1673                         }
1674                 }
1675 //#endif
1676                 if(disk[drvreg]->id[2] != secreg) {
1677                         continue;
1678                 }
1679                 if(disk[drvreg]->sector_size.sd == 0) {
1680                         continue;
1681                 }
1682                 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1683                         // id crc error
1684                         disk[drvreg]->sector_size.sd = 0;
1685                         return FDC_ST_RECNFND | FDC_ST_CRCERR;
1686                 }
1687                 
1688                 // sector found
1689                 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1690                         fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
1691                         fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
1692                 } else {
1693                         fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
1694                 }
1695                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[index];
1696                 fdc[drvreg].index = 0;
1697 //#ifdef _FDC_DEBUG_LOG
1698             if(fdc_debug_log) this->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1699                         disk[drvreg]->sector_size.sd,
1700                         disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1701                         disk[drvreg]->id[4], disk[drvreg]->id[5],
1702                         disk[drvreg]->data_crc_error ? 1 : 0);
1703 //#endif
1704                 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1705         }
1706         
1707         // sector not found
1708         disk[drvreg]->sector_size.sd = 0;
1709         return FDC_ST_RECNFND;
1710 }
1711
1712 uint8_t MB8877::search_addr()
1713 {
1714         // get track
1715         int track = fdc[drvreg].track;
1716         
1717         if(!disk[drvreg]->get_track(track, sidereg)) {
1718                 return FDC_ST_RECNFND;
1719         }
1720         
1721         // get current position
1722         int sector_num = disk[drvreg]->sector_num.sd;
1723         int position = get_cur_position();
1724         
1725         if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1726                 position -= disk[drvreg]->get_track_size();
1727         }
1728         
1729         // first scanned sector
1730         int first_sector = 0;
1731         for(int i = 0; i < sector_num; i++) {
1732                 if(position < disk[drvreg]->am1_position[i]) {
1733                         first_sector = i;
1734                         break;
1735                 }
1736         }
1737         
1738         // get sector
1739         if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1740                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
1741                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
1742                 fdc[drvreg].index = 0;
1743                 secreg = disk[drvreg]->id[0];
1744                 return 0;
1745         }
1746         
1747         // sector not found
1748         disk[drvreg]->sector_size.sd = 0;
1749         return FDC_ST_RECNFND;
1750 }
1751
1752 // ----------------------------------------------------------------------------
1753 // timing
1754 // ----------------------------------------------------------------------------
1755
1756 int MB8877::get_cur_position()
1757 {
1758         return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1759 }
1760
1761 double MB8877::get_usec_to_start_trans(bool first_sector)
1762 {
1763         // get time from current position
1764         double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
1765         if(first_sector && time < 60000 - get_passed_usec(seekend_clock)) {
1766                 time += disk[drvreg]->get_usec_per_track();
1767         }
1768         return time;
1769 }
1770
1771 double MB8877::get_usec_to_next_trans_pos(bool delay)
1772 {
1773         int position = get_cur_position();
1774         
1775         if(disk[drvreg]->invalid_format) {
1776                 // XXX: this track is invalid format and the calculated sector position may be incorrect.
1777                 // so use the constant period
1778                 return 50000;
1779         } else if(/*disk[drvreg]->no_skew && */!disk[drvreg]->correct_timing()) {
1780                 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1781                 // so use the period to search the next sector from the current position
1782                 int sector_num = disk[drvreg]->sector_num.sd;
1783                 int bytes = -1;
1784                 
1785                 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1786                         position -= disk[drvreg]->get_track_size();
1787                 }
1788                 for(int i = 0; i < sector_num; i++) {
1789                         if(position < disk[drvreg]->am1_position[i]) {
1790                                 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1791                                         bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
1792                                 } else {
1793                                         bytes = (disk[drvreg]->data_position[i] + 1) - position;
1794                                 }
1795                                 if(bytes < 0) {
1796                                         bytes += disk[drvreg]->get_track_size(); // to make sure
1797                                 }
1798                                 break;
1799                         }
1800                 }
1801                 if(bytes > 0) {
1802                         return disk[drvreg]->get_usec_per_bytes(bytes);
1803                 }
1804                 return 50000;
1805         }
1806         if(delay) {
1807                 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1808         }
1809         int bytes = fdc[drvreg].next_trans_position - position;
1810         if(fdc[drvreg].next_am1_position < position || bytes < 0) {
1811                 bytes += disk[drvreg]->get_track_size();
1812         }
1813         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1814         if(delay) {
1815                 time += DELAY_TIME;
1816         }
1817         return time;
1818 }
1819
1820 double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
1821 {
1822         int position = get_cur_position();
1823         if(delay) {
1824                 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1825         }
1826         int bytes = disk[drvreg]->get_track_size() * count - position;
1827         if(bytes < 0) {
1828                 bytes += disk[drvreg]->get_track_size();
1829         }
1830         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1831         if(delay) {
1832                 time += DELAY_TIME;
1833         }
1834         return time;
1835 }
1836
1837 // ----------------------------------------------------------------------------
1838 // irq / drq
1839 // ----------------------------------------------------------------------------
1840
1841 void MB8877::set_irq(bool val)
1842 {
1843         write_signals(&outputs_irq, val ? 0xffffffff : 0);
1844 }
1845
1846 void MB8877::set_drq(bool val)
1847 {
1848         write_signals(&outputs_drq, val ? 0xffffffff : 0);
1849 }
1850
1851 // ----------------------------------------------------------------------------
1852 // user interface
1853 // ----------------------------------------------------------------------------
1854
1855 void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
1856 {
1857         if(drv < _max_drive) {
1858                 disk[drv]->open(file_path, bank);
1859 #if defined(_USE_QT)
1860                 if((disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) || (config.disk_count_immediate[drv])) {
1861 #else
1862                 if(disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) {
1863 #endif
1864                         fdc[drv].count_immediate = true;
1865                 } else {
1866                         fdc[drv].count_immediate = false;
1867                 }
1868         }
1869 }
1870
1871 void MB8877::close_disk(int drv)
1872 {
1873         if(drv < _max_drive) {
1874                 disk[drv]->close();
1875                 cmdtype = 0;
1876                 update_head_flag(drvreg, false);
1877                 fdc[drv].count_immediate = false;
1878         }
1879 }
1880
1881 bool MB8877::is_disk_inserted(int drv)
1882 {
1883         if(drv < _max_drive) {
1884                 return disk[drv]->inserted;
1885         }
1886         return false;
1887 }
1888
1889 void MB8877::is_disk_protected(int drv, bool value)
1890 {
1891         if(drv < _max_drive) {
1892                 disk[drv]->write_protected = value;
1893         }
1894 }
1895
1896 bool MB8877::is_disk_protected(int drv)
1897 {
1898         if(drv < _max_drive) {
1899                 return disk[drv]->write_protected;
1900         }
1901         return false;
1902 }
1903
1904 uint8_t MB8877::media_type(int drv)
1905 {
1906         if(drv < _max_drive) {
1907                 if(disk[drv]->inserted) {
1908                         return disk[drv]->media_type;
1909                 }
1910         }
1911         return MEDIA_TYPE_UNK;
1912 }
1913
1914 void MB8877::set_drive_type(int drv, uint8_t type)
1915 {
1916         if(drv < _max_drive) {
1917                 disk[drv]->drive_type = type;
1918         }
1919 }
1920
1921 uint8_t MB8877::get_drive_type(int drv)
1922 {
1923         if(drv < _max_drive) {
1924                 return disk[drv]->drive_type;
1925         }
1926         return DRIVE_TYPE_UNK;
1927 }
1928
1929 void MB8877::set_drive_rpm(int drv, int rpm)
1930 {
1931         if(drv < _max_drive) {
1932                 disk[drv]->drive_rpm = rpm;
1933         }
1934 }
1935
1936 void MB8877::set_drive_mfm(int drv, bool mfm)
1937 {
1938         if(drv < _max_drive) {
1939                 disk[drv]->drive_mfm = mfm;
1940         }
1941 }
1942
1943 void MB8877::set_track_size(int drv, int size)
1944 {
1945         if(drv < _max_drive) {
1946                 disk[drv]->track_size = size;
1947         }
1948 }
1949
1950 uint8_t MB8877::fdc_status()
1951 {
1952         // for each virtual machines
1953 //#if defined(_FMR50) || defined(_FMR60)
1954         if(type_fmr50 || type_fmr60) {
1955                 return disk[drvreg]->inserted ? 2 : 0;
1956         }
1957 //#else
1958         return 0;
1959 //#endif
1960 }
1961
1962 void MB8877::update_config()
1963 {
1964         if(d_noise_seek != NULL) {
1965                 d_noise_seek->set_mute(!config.sound_noise_fdd);
1966         }
1967         if(d_noise_head_down != NULL) {
1968                 d_noise_head_down->set_mute(!config.sound_noise_fdd);
1969         }
1970         if(d_noise_head_up != NULL) {
1971                 d_noise_head_up->set_mute(!config.sound_noise_fdd);
1972         }
1973         fdc_debug_log = config.special_debug_fdc;
1974 }
1975
1976 #define STATE_VERSION   7
1977
1978 void MB8877::save_state(FILEIO* state_fio)
1979 {
1980         state_fio->FputUint32(STATE_VERSION);
1981         state_fio->FputInt32(this_device_id);
1982         
1983         state_fio->Fwrite(fdc, sizeof(fdc), 1);
1984         for(int i = 0; i < _max_drive; i++) {
1985                 disk[i]->save_state(state_fio);
1986         }
1987         state_fio->FputUint8(status);
1988         state_fio->FputUint8(status_tmp);
1989         state_fio->FputUint8(cmdreg);
1990         state_fio->FputUint8(cmdreg_tmp);
1991         state_fio->FputUint8(trkreg);
1992         state_fio->FputUint8(secreg);
1993         state_fio->FputUint8(datareg);
1994         state_fio->FputUint8(drvreg);
1995         state_fio->FputUint8(sidereg);
1996         state_fio->FputUint8(cmdtype);
1997         state_fio->Fwrite(register_id, sizeof(register_id), 1);
1998         state_fio->FputBool(now_search);
1999         state_fio->FputBool(now_seek);
2000         state_fio->FputBool(sector_changed);
2001         state_fio->FputInt32(no_command);
2002         state_fio->FputInt32(seektrk);
2003         state_fio->FputBool(seekvct);
2004         state_fio->FputBool(motor_on);
2005         state_fio->FputBool(drive_sel);
2006         state_fio->FputUint32(prev_drq_clock);
2007         state_fio->FputUint32(seekend_clock);
2008 }
2009
2010 bool MB8877::load_state(FILEIO* state_fio)
2011 {
2012         if(state_fio->FgetUint32() != STATE_VERSION) {
2013                 return false;
2014         }
2015         if(state_fio->FgetInt32() != this_device_id) {
2016                 return false;
2017         }
2018         state_fio->Fread(fdc, sizeof(fdc), 1);
2019         for(int i = 0; i < _max_drive; i++) {
2020                 if(!disk[i]->load_state(state_fio)) {
2021                         return false;
2022                 }
2023         }
2024         status = state_fio->FgetUint8();
2025         status_tmp = state_fio->FgetUint8();
2026         cmdreg = state_fio->FgetUint8();
2027         cmdreg_tmp = state_fio->FgetUint8();
2028         trkreg = state_fio->FgetUint8();
2029         secreg = state_fio->FgetUint8();
2030         datareg = state_fio->FgetUint8();
2031         drvreg = state_fio->FgetUint8();
2032         sidereg = state_fio->FgetUint8();
2033         cmdtype = state_fio->FgetUint8();
2034         state_fio->Fread(register_id, sizeof(register_id), 1);
2035         now_search = state_fio->FgetBool();
2036         now_seek = state_fio->FgetBool();
2037         sector_changed = state_fio->FgetBool();
2038         no_command = state_fio->FgetInt32();
2039         seektrk = state_fio->FgetInt32();
2040         seekvct = state_fio->FgetBool();
2041         motor_on = state_fio->FgetBool();
2042         drive_sel = state_fio->FgetBool();
2043         prev_drq_clock = state_fio->FgetUint32();
2044         seekend_clock = state_fio->FgetUint32();
2045
2046         fdc_debug_log = config.special_debug_fdc;
2047         return true;
2048 }
2049