OSDN Git Service

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