OSDN Git Service

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