OSDN Git Service

[General][VM] Merge upstream 2015-08-07 .
[csp-qt/common_source_project-fm7.git] / source / src / vm / mb8877.cpp
1 /*
2         Skelton for retropc emulator
3
4         Origin : XM7
5         Author : Takeda.Toshiya
6         Date   : 2006.12.06 -
7
8         [ MB8877 / MB8876 ]
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(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
403                                 status |= FDC_ST_WRITEP;
404                         } else {
405                                 status &= ~FDC_ST_WRITEP;
406                         }
407                         // track0, index hole
408                         if(cmdtype == FDC_CMD_TYPE1) {
409                                 if(fdc[drvreg].track == 0) {
410                                         status |= FDC_ST_TRACK00;
411                                 } else {
412                                         status &= ~FDC_ST_TRACK00;
413                                 }
414                                 // index hole signal width is 5msec (thanks Mr.Sato)
415                                 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
416                                         status |= FDC_ST_INDEX;
417                                 } else {
418                                         status &= ~FDC_ST_INDEX;
419                                 }
420                         }
421                         // show busy a moment
422                         val = status;
423                         if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
424                                 status &= ~FDC_ST_BUSY;
425 #ifdef MB8877_NO_BUSY_AFTER_SEEK
426                                 val &= ~FDC_ST_BUSY;
427 #endif
428                         }
429                 }
430                 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
431                         // MZ-2000 HuBASIC invites NOT READY status
432                         if(++no_command == 16) {
433                                 val |= FDC_ST_NOTREADY;
434                         }
435                 } else {
436                         no_command = 0;
437                 }
438                 // reset irq/drq
439                 if(!(status & FDC_ST_DRQ)) {
440                         set_drq(false);
441                 }
442                 if(!(status & FDC_ST_BUSY)) {
443                         set_irq(false);
444                 }
445 #ifdef _FDC_DEBUG_LOG
446                 //emu->out_debug_log(_T("FDCSTATUS=%2x\n"), val);
447 #endif
448 #ifdef HAS_MB8876
449                 return (~val) & 0xff;
450 #else
451                 return val;
452 #endif
453         case 1:
454                 // track reg
455 #ifdef HAS_MB8876
456                 return (~trkreg) & 0xff;
457 #else
458                 return trkreg;
459 #endif
460         case 2:
461                 // sector reg
462 #ifdef HAS_MB8876
463                 return (~secreg) & 0xff;
464 #else
465                 return secreg;
466 #endif
467         case 3:
468                 // data reg
469                 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
470                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
471                                 // read or multisector read
472                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
473                                         datareg = disk[drvreg]->sector[fdc[drvreg].index];
474                                         //fdc[drvreg].index++;
475                                 }
476                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
477
478                                         if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
479                                                 // data crc error
480 #ifdef _FDC_DEBUG_LOG
481                                                 emu->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
482 #endif
483                                                 status |= FDC_ST_CRCERR;
484                                                 status &= ~FDC_ST_BUSY;
485                                                 cmdtype = 0;
486                                                 set_irq(true);
487                                         } else if(cmdtype == FDC_CMD_RD_SEC) {
488                                                 // single sector
489 #ifdef _FDC_DEBUG_LOG
490                                                 emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
491 #endif
492                                                 status &= ~FDC_ST_BUSY;
493                                                 cmdtype = 0;
494                                                 set_irq(true);
495                                         } else {
496                                                 // multisector
497 #ifdef _FDC_DEBUG_LOG
498                                                 emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
499 #endif
500                                                 register_my_event(EVENT_MULTI1, 30);
501                                                 register_my_event(EVENT_MULTI2, 60);
502                                         }
503                                 } else {
504                                         register_drq_event(1);
505                                 }
506                                 status &= ~FDC_ST_DRQ;
507                         } else if(cmdtype == FDC_CMD_RD_ADDR) {
508                                 // read address
509                                 if(fdc[drvreg].index < 6) {
510                                         datareg = disk[drvreg]->id[fdc[drvreg].index];
511                                         //fdc[drvreg].index++;
512                                 }
513                                 if((fdc[drvreg].index + 1) >= 6) {
514                                         if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
515                                                 // id crc error
516                                                 status |= FDC_ST_CRCERR;
517                                         }
518                                         status &= ~FDC_ST_BUSY;
519                                         cmdtype = 0;
520                                         set_irq(true);
521 #ifdef _FDC_DEBUG_LOG
522                                         emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
523 #endif
524                                 } else {
525                                         register_drq_event(1);
526                                 }
527                                 status &= ~FDC_ST_DRQ;
528                         } else if(cmdtype == FDC_CMD_RD_TRK) {
529                                 // read track
530                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
531                                         datareg = disk[drvreg]->track[fdc[drvreg].index];
532                                         //fdc[drvreg].index++;
533                                 }
534                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
535 #ifdef _FDC_DEBUG_LOG
536                                         emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));
537 #endif
538                                         status &= ~FDC_ST_BUSY;
539                                         status |= FDC_ST_LOSTDATA;
540                                         cmdtype = 0;
541                                         set_irq(true);
542 #ifdef _FDC_DEBUG_LOG
543                                         emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
544 #endif
545                                 } else {
546                                         register_drq_event(1);
547                                 }
548                                 status &= ~FDC_ST_DRQ;
549                         }
550                         if(!(status & FDC_ST_DRQ)) {
551                                 cancel_my_event(EVENT_LOST);
552                                 set_drq(false);
553                                 fdc[drvreg].access = true;
554                         }
555                 }
556 #ifdef _FDC_DEBUG_LOG
557                 //emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
558 #endif
559 #ifdef HAS_MB8876
560                 return (~datareg) & 0xff;
561 #else
562                 return datareg;
563 #endif
564         }
565         return 0xff;
566 }
567
568 void MB8877::write_dma_io8(uint32 addr, uint32 data)
569 {
570         write_io8(3, data);
571 }
572
573 uint32 MB8877::read_dma_io8(uint32 addr)
574 {
575         return read_io8(3);
576 }
577
578 void MB8877::write_signal(int id, uint32 data, uint32 mask)
579 {
580         if(id == SIG_MB8877_DRIVEREG) {
581                 drvreg = data & DRIVE_MASK;
582                 drive_sel = true;
583                 seekend_clock = current_clock();
584         } else if(id == SIG_MB8877_SIDEREG) {
585                 sidereg = (data & mask) ? 1 : 0;
586         } else if(id == SIG_MB8877_MOTOR) {
587                 motor_on = ((data & mask) != 0);
588         }
589 }
590
591 uint32 MB8877::read_signal(int ch)
592 {
593         // get access status
594         uint32 stat = 0;
595
596         for(int i = 0; i < MAX_DRIVE; i++) {
597                 if(fdc[i].access) {
598                         stat |= 1 << i;
599                 }
600                 fdc[i].access = false;
601         }
602         if(now_search) {
603                 stat |= 1 << drvreg;
604         }
605         return stat;
606 }
607
608 void MB8877::event_callback(int event_id, int err)
609 {
610         int event = event_id >> 8;
611         int cmd = event_id & 0xff;
612         register_id[event] = -1;
613         
614         // cancel event if the command is finished or other command is executed
615         if(cmd != cmdtype) {
616                 if(event == EVENT_SEEK) {
617                         now_seek = false;
618                 } else if(event == EVENT_SEARCH) {
619                         now_search = false;
620                 }
621                 return;
622         }
623         
624         switch(event) {
625         case EVENT_SEEK:
626 #ifdef _FDC_DEBUG_LOG
627                 //emu->out_debug_log(_T("FDC\tSEEK START\n"));
628 #endif
629                 if(seektrk > fdc[drvreg].track) {
630                         fdc[drvreg].track++;
631                 } else if(seektrk < fdc[drvreg].track) {
632                         fdc[drvreg].track--;
633                 }
634                 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
635                         trkreg = fdc[drvreg].track;
636                 }
637                 if(seektrk == fdc[drvreg].track) {
638                         // auto update
639                         //if((cmdreg & 0xf0) == 0) {
640                                 datareg = seektrk;
641                         //}
642                         if(cmdreg & 4) {
643                                 status |= search_track();
644                         }
645                         now_seek = false;
646                         seekend_clock = current_clock();
647                         set_irq(true);
648 #ifdef _FDC_DEBUG_LOG
649                         emu->out_debug_log(_T("FDC\tSEEK\n"));
650 #endif
651                 } else {
652                         register_seek_event();
653                 }
654                 break;
655         case EVENT_SEEKEND:
656                 if(seektrk == fdc[drvreg].track) {
657                         // auto update
658                         if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
659                                 trkreg = fdc[drvreg].track;
660                         }
661                         //if((cmdreg & 0xf0) == 0) {
662                                 datareg = seektrk;
663                         //}
664                         if(cmdreg & 4) {
665                                 status |= search_track();
666                         }
667                         now_seek = false;
668                         seekend_clock = current_clock();
669 //                      cancel_my_event(EVENT_SEEK);
670                         set_irq(true);
671 #ifdef _FDC_DEBUG_LOG
672                         emu->out_debug_log(_T("FDC\tSEEK END\n"));
673 #endif
674                 }
675                 break;
676         case EVENT_SEARCH:
677                 now_search = false;
678                 if(status_tmp & FDC_ST_RECNFND) {
679 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
680                         // for SHARP X1 Batten Tanuki
681                         if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
682                                 status_tmp &= ~FDC_ST_RECNFND;
683                         }
684 #endif
685 #ifdef _FDC_DEBUG_LOG
686                         emu->out_debug_log(_T("FDC\tSEARCH NG\n"));
687 #endif
688                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
689                         cmdtype = 0;
690                         set_irq(true);
691                 } else if(status_tmp & FDC_ST_WRITEFAULT) {
692 #ifdef _FDC_DEBUG_LOG
693                         emu->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
694 #endif
695                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
696                         cmdtype = 0;
697                         set_irq(true);
698                 } else {
699                         status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
700                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
701                                 register_lost_event(8);
702                         } else if(cmdtype == FDC_CMD_WR_TRK) {
703                                 register_lost_event(3);
704                         } else {
705                                 register_lost_event(1);
706                         }
707                         fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
708                         fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
709                         set_drq(true);
710                         drive_sel = false;
711 #ifdef _FDC_DEBUG_LOG
712                         emu->out_debug_log(_T("FDC\tSEARCH OK\n"));
713 #endif
714                 }
715                 break;
716         case EVENT_DRQ:
717                 if(status & FDC_ST_BUSY) {
718                         status |= FDC_ST_DRQ;
719                         register_lost_event(1);
720                         if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
721                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
722                         } else {
723                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
724                         }
725                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
726                            cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
727                            cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK  ||
728                            cmdtype == FDC_CMD_RD_ADDR) {
729                                 fdc[drvreg].index++;
730                         }
731                         fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
732                         set_drq(true);
733 #ifdef _FDC_DEBUG_LOG
734                         //emu->out_debug_log(_T("FDC\tDRQ!\n"));
735 #endif
736                 }
737                 break;
738         case EVENT_MULTI1:
739                 secreg++;
740                 break;
741         case EVENT_MULTI2:
742                 if(cmdtype == FDC_CMD_RD_MSEC) {
743                         cmd_readdata(false);
744                 } else if(cmdtype == FDC_CMD_WR_MSEC) {
745                         cmd_writedata(false);
746                 }
747                 break;
748         case EVENT_LOST:
749                 if(status & FDC_ST_BUSY) {
750 #ifdef _FDC_DEBUG_LOG
751                         emu->out_debug_log("FDC\tDATA LOST\n");
752 #endif
753                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
754                                 if(fdc[drvreg].index == 0) {
755                                         status &= ~FDC_ST_BUSY;
756                                         //status &= ~FDC_ST_DRQ;
757                                         cmdtype = 0;
758                                         set_irq(true);
759                                         //set_drq(false);
760                                 } else {
761                                         write_io8(3, 0x00);
762                                 }
763                         } else {
764                                 read_io8(3);
765                         }
766                         status |= FDC_ST_LOSTDATA;
767                 }
768                 break;
769         }
770 }
771
772 // ----------------------------------------------------------------------------
773 // command
774 // ----------------------------------------------------------------------------
775
776 void MB8877::process_cmd()
777 {
778 #ifdef _FDC_DEBUG_LOG
779         static const _TCHAR *cmdstr[0x10] = {
780                 _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),
781                 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
782                 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
783                 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
784         };
785         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);
786 #endif
787         set_irq(false);
788         
789         switch(cmdreg & 0xf0) {
790         // type-1
791         case 0x00:
792                 cmd_restore();
793                 break;
794         case 0x10:
795                 cmd_seek();
796                 break;
797         case 0x20:
798         case 0x30:
799                 cmd_step();
800                 break;
801         case 0x40:
802         case 0x50:
803                 cmd_stepin();
804                 break;
805         case 0x60:
806         case 0x70:
807                 cmd_stepout();
808                 break;
809         // type-2
810         case 0x80:
811         case 0x90:
812                 cmd_readdata(true);
813                 break;
814         case 0xa0:
815         case 0xb0:
816                 cmd_writedata(true);
817                 break;
818         // type-3
819         case 0xc0:
820                 cmd_readaddr();
821                 break;
822         case 0xe0:
823                 cmd_readtrack();
824                 break;
825         case 0xf0:
826                 cmd_writetrack();
827                 break;
828         // type-4
829         case 0xd0:
830                 cmd_forceint();
831                 break;
832         default:
833                 break;
834         }
835 }
836
837 void MB8877::cmd_restore()
838 {
839         // type-1 restore
840         cmdtype = FDC_CMD_TYPE1;
841         status = FDC_ST_HEADENG | FDC_ST_BUSY;
842         trkreg = 0xff;
843         
844         seektrk = 0;
845         seekvct = true;
846         if(fdc[drvreg].track != seektrk) {
847                 cancel_my_event(EVENT_SEEKEND);
848                 register_seek_event();
849         } else {
850                 cancel_my_event(EVENT_SEEK);
851 //              register_my_event(EVENT_SEEKEND, 300);
852                 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
853         }
854
855 #ifdef _FDC_DEBUG_LOG
856         emu->out_debug_log(_T("FDC\tSEEKn"));
857 #endif
858 }
859
860 void MB8877::cmd_seek()
861 {
862         // type-1 seek
863         cmdtype = FDC_CMD_TYPE1;
864         status = FDC_ST_HEADENG | FDC_ST_BUSY;
865         
866         seektrk = datareg;
867         seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
868         seekvct = !(datareg > trkreg);
869         
870         if(fdc[drvreg].track != seektrk) {
871                 cancel_my_event(EVENT_SEEKEND);
872                 register_seek_event();
873         } else {
874                 cancel_my_event(EVENT_SEEK);
875 //              register_my_event(EVENT_SEEKEND, 300);
876                 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
877         }
878 #ifdef _FDC_DEBUG_LOG
879         emu->out_debug_log(_T("FDC\tSEEKn"));
880 #endif
881 }
882
883 void MB8877::cmd_step()
884 {
885         // type-1 step
886         if(seekvct) {
887                 cmd_stepout();
888         } else {
889                 cmd_stepin();
890         }
891 }
892
893 void MB8877::cmd_stepin()
894 {
895         // type-1 step in
896         cmdtype = FDC_CMD_TYPE1;
897         status = FDC_ST_HEADENG | FDC_ST_BUSY;
898         
899         seektrk = (fdc[drvreg].track < 83) ? fdc[drvreg].track + 1 : 83;
900         seekvct = false;
901         
902         if(fdc[drvreg].track != seektrk) {
903                 cancel_my_event(EVENT_SEEKEND);
904                 register_seek_event();
905         } else {
906                 cancel_my_event(EVENT_SEEK);
907 //              register_my_event(EVENT_SEEKEND, 300);
908                 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
909         }
910 #ifdef _FDC_DEBUG_LOG
911         emu->out_debug_log(_T("FDC\tSEEKn"));
912 #endif
913 }
914
915 void MB8877::cmd_stepout()
916 {
917         // type-1 step out
918         cmdtype = FDC_CMD_TYPE1;
919         status = FDC_ST_HEADENG | FDC_ST_BUSY;
920         
921         seektrk = (fdc[drvreg].track > 0) ? fdc[drvreg].track - 1 : 0;
922         seekvct = true;
923         
924         if(fdc[drvreg].track != seektrk) {
925                 cancel_my_event(EVENT_SEEKEND);
926                 register_seek_event();
927         } else {
928                 cancel_my_event(EVENT_SEEK);
929 //              register_my_event(EVENT_SEEKEND, 300);
930                 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
931         }
932 #ifdef _FDC_DEBUG_LOG
933         emu->out_debug_log(_T("FDC\tSEEKn"));
934 #endif
935 }
936
937 void MB8877::cmd_readdata(bool first_sector)
938 {
939         // type-2 read data
940         cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
941         int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;
942         status = FDC_ST_BUSY;
943         //status_tmp = search_sector(trkreg, side, secreg, ((cmdreg & 2) != 0));
944         status_tmp = search_sector();
945         now_search = true;
946         
947         double time;
948         if(status_tmp & FDC_ST_RECNFND) {
949                 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
950                 time = disk[drvreg]->get_usec_per_bytes(bytes);
951         } else {
952                 time = get_usec_to_start_trans(first_sector);
953         }
954         register_my_event(EVENT_SEARCH, time);
955         cancel_my_event(EVENT_LOST);
956 }
957
958 void MB8877::cmd_writedata(bool first_sector)
959 {
960         // type-2 write data
961         cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
962         status = FDC_ST_BUSY;
963         status_tmp = search_sector() & ~FDC_ST_RECTYPE;
964         now_search = true;
965         
966         double time;
967         if(status_tmp & FDC_ST_RECNFND) {
968                 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
969                 time = disk[drvreg]->get_usec_per_bytes(bytes);
970         } else if(status & FDC_ST_WRITEFAULT) {
971                 time = (cmdreg & 4) ? 15000 : 1;
972         } else {
973                 time = get_usec_to_start_trans(first_sector);
974         }
975         register_my_event(EVENT_SEARCH, time);
976         cancel_my_event(EVENT_LOST);
977 }
978
979 void MB8877::cmd_readaddr()
980 {
981         // type-3 read address
982         cmdtype = FDC_CMD_RD_ADDR;
983         status = FDC_ST_BUSY;
984         status_tmp = search_addr();
985         now_search = true;
986         
987         double time;
988         if(status_tmp & FDC_ST_RECNFND) {
989                 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
990                 time = disk[drvreg]->get_usec_per_bytes(bytes);
991         } else {
992                 time = get_usec_to_start_trans(true);
993         }
994         register_my_event(EVENT_SEARCH, time);
995         cancel_my_event(EVENT_LOST);
996 }
997
998 void MB8877::cmd_readtrack()
999 {
1000         // type-3 read track
1001         cmdtype = FDC_CMD_RD_TRK;
1002         status = FDC_ST_BUSY;
1003         status_tmp = 0;
1004         
1005         disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1006         fdc[drvreg].index = 0;
1007         now_search = true;
1008         
1009         fdc[drvreg].next_trans_position = 0;
1010         int bytes = disk[drvreg]->get_track_size() - get_cur_position();
1011         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1012         
1013         // wait at least 15msec before check index hole raise first drq
1014         if((cmdreg & 4) && time < 15000) {
1015                 time += disk[drvreg]->get_usec_per_track();
1016         }
1017         register_my_event(EVENT_SEARCH, time);
1018         cancel_my_event(EVENT_LOST);
1019 }
1020
1021 void MB8877::cmd_writetrack()
1022 {
1023         // type-3 write track
1024         cmdtype = FDC_CMD_WR_TRK;
1025         status = FDC_ST_BUSY;
1026         status_tmp = 0;
1027         
1028         fdc[drvreg].index = 0;
1029         fdc[drvreg].id_written = false;
1030         now_search = true;
1031         
1032         double time;
1033         if(disk[drvreg]->write_protected) {
1034                 status_tmp = FDC_ST_WRITEFAULT;
1035                 time = (cmdreg & 4) ? 15000 : 1;
1036         } else {
1037                 if(cmdreg & 4) {
1038                         // wait 15msec before raise first drq
1039                         fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(15000)) % disk[drvreg]->get_track_size();
1040                         time = 15000;
1041                 } else {
1042                         // raise first drq soon
1043                         fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1044                         time = disk[drvreg]->get_usec_per_bytes(1);
1045                 }
1046                 // wait at least 3bytes before check index hole and raise second drq
1047                 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1048                 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1049                         fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1050                 }
1051         }
1052         register_my_event(EVENT_SEARCH, time);
1053         cancel_my_event(EVENT_LOST);
1054 }
1055
1056 void MB8877::cmd_forceint()
1057 {
1058         // type-4 force interrupt
1059         if(cmdtype == FDC_CMD_WR_TRK) {
1060                 if(!disk[drvreg]->write_protected) {
1061                         if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1062                                 // data mark of last sector is not written
1063                                 disk[drvreg]->set_data_mark_missing();
1064                         }
1065                         disk[drvreg]->sync_buffer();
1066                 }
1067         }
1068         if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1069                 cmdtype = FDC_CMD_TYPE1;
1070                 status = FDC_ST_HEADENG;
1071         }
1072         status &= ~FDC_ST_BUSY;
1073         
1074         // force interrupt if bit0-bit3 is high
1075         if(cmdreg & 0x0f) {
1076                 set_irq(true);
1077         }
1078         
1079         // finish current seeking
1080         if(now_seek) {
1081                 if(seektrk > fdc[drvreg].track) {
1082                         fdc[drvreg].track++;
1083                 } else if(seektrk < fdc[drvreg].track) {
1084                         fdc[drvreg].track--;
1085                 }
1086                 if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
1087                         trkreg = fdc[drvreg].track;
1088                 }
1089                 if(seektrk == fdc[drvreg].track) {
1090                         // auto update
1091                         if((cmdreg_tmp & 0xf0) == 0) {
1092                                 datareg = 0;
1093                         }
1094                 }
1095         }
1096         now_search = now_seek = false;
1097         
1098         cancel_my_event(EVENT_SEEK);
1099         cancel_my_event(EVENT_SEEKEND);
1100         cancel_my_event(EVENT_SEARCH);
1101         cancel_my_event(EVENT_DRQ);
1102         cancel_my_event(EVENT_MULTI1);
1103         cancel_my_event(EVENT_MULTI2);
1104         cancel_my_event(EVENT_LOST);
1105 }
1106
1107 // ----------------------------------------------------------------------------
1108 // media handler
1109 // ----------------------------------------------------------------------------
1110
1111 uint8 MB8877::search_track()
1112 {
1113         // get track
1114         if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)){
1115                 return FDC_ST_SEEKERR;
1116         }
1117         
1118         // verify track number
1119         if(disk[drvreg]->ignore_crc()) {
1120                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1121                         disk[drvreg]->get_sector(-1, -1, i);
1122                         if(disk[drvreg]->id[0] == trkreg) {
1123                                 return 0;
1124                         }
1125                 }
1126         } else {
1127                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1128                         disk[drvreg]->get_sector(-1, -1, i);
1129                         if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1130                                 return 0;
1131                         }
1132                 }
1133                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1134                         disk[drvreg]->get_sector(-1, -1, i);
1135                         if(disk[drvreg]->id[0] == trkreg) {
1136                                 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1137                         }
1138                 }
1139         }
1140         return FDC_ST_SEEKERR;
1141 }
1142
1143 uint8 MB8877::search_sector()
1144 {
1145         // write protect
1146         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1147                 if(disk[drvreg]->write_protected) {
1148                         return FDC_ST_WRITEFAULT;
1149                 }
1150         }
1151         
1152         // get track
1153         if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1154                 return FDC_ST_RECNFND;
1155         }
1156         
1157         // get current position
1158         int sector_num = disk[drvreg]->sector_num.sd;
1159         int position = get_cur_position();
1160         
1161         if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1162                 position -= disk[drvreg]->get_track_size();
1163         }
1164         
1165         // first scanned sector
1166         int first_sector = 0;
1167         for(int i = 0; i < sector_num; i++) {
1168                 if(position < disk[drvreg]->sync_position[i]) {
1169                         first_sector = i;
1170                         break;
1171                 }
1172         }
1173         
1174         // scan sectors
1175         for(int i = 0; i < sector_num; i++) {
1176                 // get sector
1177                 int index = (first_sector + i) % sector_num;
1178                 disk[drvreg]->get_sector(-1, -1, index);
1179                 
1180                 // check id
1181 //              if(disk[drvreg]->id[0] != trkreg) {
1182 //                      continue;
1183 //              }
1184                 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1185                         continue;
1186                 }
1187                 if(disk[drvreg]->id[2] != secreg) {
1188                         continue;
1189                 }
1190                 if(disk[drvreg]->sector_size.sd == 0) {
1191                         continue;
1192                 }
1193                 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1194                         // id crc error
1195                         disk[drvreg]->sector_size.sd = 0;
1196                         return FDC_ST_RECNFND | FDC_ST_CRCERR;
1197                 }
1198                 
1199                 // sector found
1200                 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1201                         fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1202                         fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[i] - fdc[drvreg].next_trans_position;
1203                 } else {
1204                         fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i];
1205                 }
1206                 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[i];
1207                 fdc[drvreg].index = 0;
1208 #ifdef _FDC_DEBUG_LOG
1209                 emu->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1210                                                    disk[drvreg]->sector_size.sd,
1211                                                    disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1212                                                    disk[drvreg]->id[4], disk[drvreg]->id[5],
1213                                                    disk[drvreg]->crc_error ? 1 : 0);
1214 #endif
1215                 //return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0) | ((disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0);
1216                 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1217         }
1218         
1219         // sector not found
1220         disk[drvreg]->sector_size.sd = 0;
1221         return FDC_ST_RECNFND;
1222 }
1223
1224 uint8 MB8877::search_addr()
1225 {
1226         // get track
1227         if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1228                 return FDC_ST_RECNFND;
1229         }
1230         
1231         // get current position
1232         int sector_num = disk[drvreg]->sector_num.sd;
1233         int position = get_cur_position();
1234         
1235         if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1236                 position -= disk[drvreg]->get_track_size();
1237         }
1238         
1239         // first scanned sector
1240         int first_sector = 0;
1241         for(int i = 0; i < sector_num; i++) {
1242                 if(position < disk[drvreg]->sync_position[i]) {
1243                         first_sector = i;
1244                         break;
1245                 }
1246         }
1247         
1248         // get sector
1249         if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1250                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector];
1251                 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[first_sector];
1252                 fdc[drvreg].index = 0;
1253                 secreg = disk[drvreg]->id[0];
1254                 //return (disk[drvreg]->crc_error && !config.ignore_crc[drvreg]) ? FDC_ST_CRCERR : 0;
1255                 //return (disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0;
1256                 return 0;
1257         }
1258         
1259         // sector not found
1260         disk[drvreg]->sector_size.sd = 0;
1261         return FDC_ST_RECNFND;
1262 }
1263
1264 // ----------------------------------------------------------------------------
1265 // timing
1266 // ----------------------------------------------------------------------------
1267
1268 int MB8877::get_cur_position()
1269 {
1270         return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1271 }
1272
1273 double MB8877::get_usec_to_start_trans(bool first_sector)
1274 {
1275 #if defined(_X1TURBO) || defined(_X1TURBOZ)
1276         // FIXME: ugly patch for X1turbo ALPHA
1277         if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
1278                 return 100;
1279         } else
1280 #endif
1281         if(disk[drvreg]->no_skew && !disk[drvreg]->correct_timing()) {
1282                 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1283                 // so use the constant period to search the target sector
1284                 return 50000;
1285         }
1286         
1287         // get time from current position
1288         int position = get_cur_position();
1289         int bytes = fdc[drvreg].next_trans_position - position;
1290         if(fdc[drvreg].next_sync_position < position || bytes < 0) {
1291                 bytes += disk[drvreg]->get_track_size();
1292         }
1293         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1294    
1295         
1296         if(first_sector) {
1297                 // wait at least 15msec before search target sector and rqise first drq
1298                 if((cmdreg & 4) && time < 15000) {
1299                         time += disk[drvreg]->get_usec_per_track();
1300                 }
1301                 // wait at least 60msec before search target sector and rqise first drq just after seek command is done or drive register is written
1302                 if(time < 60000 - passed_usec(seekend_clock)) {
1303                         time += disk[drvreg]->get_usec_per_track();
1304                 }
1305         }
1306         return time;
1307 }
1308
1309 // ----------------------------------------------------------------------------
1310 // irq / drq
1311 // ----------------------------------------------------------------------------
1312
1313 void MB8877::set_irq(bool val)
1314 {
1315         write_signals(&outputs_irq, val ? 0xffffffff : 0);
1316 }
1317
1318 void MB8877::set_drq(bool val)
1319 {
1320         write_signals(&outputs_drq, val ? 0xffffffff : 0);
1321 }
1322
1323 // ----------------------------------------------------------------------------
1324 // user interface
1325 // ----------------------------------------------------------------------------
1326
1327 void MB8877::open_disk(int drv, _TCHAR path[], int bank)
1328 {
1329         if(drv < MAX_DRIVE) {
1330                 disk[drv]->open(path, bank);
1331 #ifdef _FDC_DEBUG_LOG
1332                 emu->out_debug_log(_T("FDC\tOPEN DISK#%d\n"), drv);
1333 #endif
1334         }
1335 }
1336
1337 void MB8877::close_disk(int drv)
1338 {
1339         if(drv < MAX_DRIVE) {
1340                 disk[drv]->close();
1341                 cmdtype = 0;
1342 #ifdef _FDC_DEBUG_LOG
1343                 emu->out_debug_log(_T("FDC\tCLOSE DISK#%d\n"), drv);
1344 #endif
1345         }
1346 }
1347
1348 bool MB8877::disk_inserted(int drv)
1349 {
1350         if(drv < MAX_DRIVE) {
1351                 return disk[drv]->inserted;
1352         }
1353         return false;
1354 }
1355
1356 void MB8877::set_disk_protected(int drv, bool value)
1357 {
1358         if(drv < MAX_DRIVE) {
1359                 disk[drv]->write_protected = value;
1360         }
1361 }
1362
1363 bool MB8877::get_disk_protected(int drv)
1364 {
1365         if(drv < MAX_DRIVE) {
1366                 return disk[drv]->write_protected;
1367         }
1368         return false;
1369 }
1370
1371 void MB8877::set_drive_type(int drv, uint8 type)
1372 {
1373         if(drv < MAX_DRIVE) {
1374                 disk[drv]->drive_type = type;
1375         }
1376 }
1377
1378 uint8 MB8877::get_drive_type(int drv)
1379 {
1380         if(drv < MAX_DRIVE) {
1381                 return disk[drv]->drive_type;
1382         }
1383         return DRIVE_TYPE_UNK;
1384 }
1385
1386 void MB8877::set_drive_rpm(int drv, int rpm)
1387 {
1388         if(drv < MAX_DRIVE) {
1389                 disk[drv]->drive_rpm = rpm;
1390         }
1391 }
1392
1393 void MB8877::set_drive_mfm(int drv, bool mfm)
1394 {
1395         if(drv < MAX_DRIVE) {
1396                 disk[drv]->drive_mfm = mfm;
1397         }
1398 }
1399
1400 uint8 MB8877::fdc_status()
1401 {
1402         // for each virtual machines
1403 #if defined(_FMR50) || defined(_FMR60)
1404         return disk[drvreg]->inserted ? 2 : 0;
1405 #else
1406         return 0;
1407 #endif
1408 }
1409
1410 #define STATE_VERSION   4
1411
1412 void MB8877::save_state(FILEIO* state_fio)
1413 {
1414         state_fio->FputUint32(STATE_VERSION);
1415         state_fio->FputInt32(this_device_id);
1416         
1417         state_fio->Fwrite(fdc, sizeof(fdc), 1);
1418         for(int i = 0; i < MAX_DRIVE; i++) {
1419                 disk[i]->save_state(state_fio);
1420         }
1421         state_fio->FputUint8(status);
1422         state_fio->FputUint8(status_tmp);
1423         state_fio->FputUint8(cmdreg);
1424         state_fio->FputUint8(cmdreg_tmp);
1425         state_fio->FputUint8(trkreg);
1426         state_fio->FputUint8(secreg);
1427         state_fio->FputUint8(datareg);
1428         state_fio->FputUint8(drvreg);
1429         state_fio->FputUint8(sidereg);
1430         state_fio->FputUint8(cmdtype);
1431         state_fio->Fwrite(register_id, sizeof(register_id), 1);
1432         state_fio->FputBool(now_search);
1433         state_fio->FputBool(now_seek);
1434         state_fio->FputInt32(no_command);
1435         state_fio->FputInt32(seektrk);
1436         state_fio->FputBool(seekvct);
1437         state_fio->FputBool(motor_on);
1438         state_fio->FputBool(drive_sel);
1439         state_fio->FputUint32(prev_drq_clock);
1440         state_fio->FputUint32(seekend_clock);
1441 }
1442
1443 bool MB8877::load_state(FILEIO* state_fio)
1444 {
1445         if(state_fio->FgetUint32() != STATE_VERSION) {
1446                 return false;
1447         }
1448         if(state_fio->FgetInt32() != this_device_id) {
1449                 return false;
1450         }
1451         state_fio->Fread(fdc, sizeof(fdc), 1);
1452         for(int i = 0; i < MAX_DRIVE; i++) {
1453                 if(!disk[i]->load_state(state_fio)) {
1454                         return false;
1455                 }
1456         }
1457         status = state_fio->FgetUint8();
1458         status_tmp = state_fio->FgetUint8();
1459         cmdreg = state_fio->FgetUint8();
1460         cmdreg_tmp = state_fio->FgetUint8();
1461         trkreg = state_fio->FgetUint8();
1462         secreg = state_fio->FgetUint8();
1463         datareg = state_fio->FgetUint8();
1464         drvreg = state_fio->FgetUint8();
1465         sidereg = state_fio->FgetUint8();
1466         cmdtype = state_fio->FgetUint8();
1467         state_fio->Fread(register_id, sizeof(register_id), 1);
1468         now_search = state_fio->FgetBool();
1469         now_seek = state_fio->FgetBool();
1470         no_command = state_fio->FgetInt32();
1471         seektrk = state_fio->FgetInt32();
1472         seekvct = state_fio->FgetBool();
1473         motor_on = state_fio->FgetBool();
1474         drive_sel = state_fio->FgetBool();
1475         prev_drq_clock = state_fio->FgetUint32();
1476         seekend_clock = state_fio->FgetUint32();
1477         return true;
1478 }
1479