OSDN Git Service

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