OSDN Git Service

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