OSDN Git Service

[VM][DEVICE] Selectable building separating or not separating.
[csp-qt/common_source_project-fm7.git] / source / src / vm / mb8877.cpp
1 /*
2         Skelton for retropc emulator
3
4         Origin : XM7
5         Author : Takeda.Toshiya
6         Date   : 2006.12.06 -
7
8         [ MB8877 / MB8876 / MB8866 / MB89311 ]
9 */
10
11 #include "mb8877.h"
12 #include "disk.h"
13
14 #define FDC_ST_BUSY             0x01    // busy
15 #define FDC_ST_INDEX            0x02    // index hole
16 #define FDC_ST_DRQ              0x02    // data request
17 #define FDC_ST_TRACK00          0x04    // track0
18 #define FDC_ST_LOSTDATA         0x04    // data lost
19 #define FDC_ST_CRCERR           0x08    // crc error
20 #define FDC_ST_SEEKERR          0x10    // seek error
21 #define FDC_ST_RECNFND          0x10    // sector not found
22 #define FDC_ST_HEADENG          0x20    // head engage
23 #define FDC_ST_RECTYPE          0x20    // record type
24 #define FDC_ST_WRITEFAULT       0x20    // write fault
25 #define FDC_ST_WRITEP           0x40    // write protectdc
26 #define FDC_ST_NOTREADY         0x80    // media not inserted
27
28 #define FDC_CMD_TYPE1           1
29 #define FDC_CMD_RD_SEC          2
30 #define FDC_CMD_RD_MSEC         3
31 #define FDC_CMD_WR_SEC          4
32 #define FDC_CMD_WR_MSEC         5
33 #define FDC_CMD_RD_ADDR         6
34 #define FDC_CMD_RD_TRK          7
35 #define FDC_CMD_WR_TRK          8
36
37 #define EVENT_SEEK              0
38 #define EVENT_SEEKEND           1
39 #define EVENT_SEARCH            2
40 #define EVENT_DRQ               3
41 #define EVENT_MULTI1            4
42 #define EVENT_MULTI2            5
43 #define EVENT_LOST              6
44 #define EVENT_MOTOR 7
45
46 #define DRIVE_MASK              (MAX_DRIVE - 1)
47
48 #define DELAY_TIME              (disk[drvreg]->drive_type == DRIVE_TYPE_2HD ? 15000 : 30000)
49
50 static const int seek_wait_hi[4] = {3000,  6000, 10000, 16000}; // 2MHz
51 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000}; // 1MHz
52
53 void MB8877::cancel_my_event(int event)
54 {
55         if(register_id[event] != -1) {
56                 cancel_event(this, register_id[event]);
57                 register_id[event] = -1;
58         }
59 }
60
61 void MB8877::register_my_event(int event, double usec)
62 {
63         cancel_my_event(event);
64         register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, &register_id[event]);
65 }
66
67 void MB8877::register_seek_event()
68 {
69
70         cancel_my_event(EVENT_SEEK);
71         if(fdc[drvreg].track == seektrk) {
72                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), 1, false, &register_id[EVENT_SEEK]);
73         } else if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
74                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, &register_id[EVENT_SEEK]);
75         } else {
76                 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, &register_id[EVENT_SEEK]);
77         }
78         now_seek = true;
79 }
80
81 void MB8877::register_drq_event(int bytes)
82 {
83         double usec = disk[drvreg]->get_usec_per_bytes(bytes) - get_passed_usec(prev_drq_clock);
84         if(usec < 4) {
85                 usec = 4;
86         }
87 #if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
88         if((disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER) ||
89            (disk[drvreg]->is_special_disk == SPECIAL_DISK_FM77AV_PSYOBLADE)) {
90                 usec = 4;
91         }
92 #endif
93         cancel_my_event(EVENT_DRQ);
94         register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, &register_id[EVENT_DRQ]);
95 }
96
97 void MB8877::register_lost_event(int bytes)
98 {
99         cancel_my_event(EVENT_LOST);
100         register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, &register_id[EVENT_LOST]);
101 }
102
103
104
105 void MB8877::initialize()
106 {
107         // initialize d88 handler
108         for(int i = 0; i < MAX_DRIVE; i++) {
109                 _TCHAR strbuf[128];
110                 _TCHAR strbuf2[128];
111                 disk[i] = new DISK(emu);
112                 strncpy(strbuf, (_TCHAR *)this->get_device_name(), 128);
113                 snprintf(strbuf2, 128, "%s/DRIVE%d", strbuf, i + 1);
114                 disk[i]->set_device_name((const _TCHAR *)strbuf2);
115         }
116         
117         // initialize timing
118         memset(fdc, 0, sizeof(fdc));
119         
120         // initialize fdc
121         seektrk = 0;
122         seekvct = true;
123         status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
124         drvreg = 0;
125         prev_drq_clock = seekend_clock = 0;
126 }
127
128 void MB8877::release()
129 {
130         // release d88 handler
131         for(int i = 0; i < MAX_DRIVE; i++) {
132                 if(disk[i]) {
133                         disk[i]->close();
134                         delete disk[i];
135                 }
136         }
137 }
138
139 void MB8877::reset()
140 {
141         touch_sound();
142         for(int i = 0; i < MAX_DRIVE; i++) {
143                 fdc[i].track = 0;
144                 fdc[i].index = 0;
145                 fdc[i].access = false;
146         }
147         for(int i = 0; i < (int)array_length(register_id); i++) {
148                 register_id[i] = -1;
149         }
150         now_search = now_seek = drive_sel = false;
151         no_command = 0;
152 #ifdef HAS_MB89311
153         extended_mode = true;
154 #endif
155 }
156
157 void MB8877::write_io8(uint32_t addr, uint32_t data)
158 {
159         bool _stat;
160         switch(addr & 3) {
161         case 0:
162                 // command reg
163                 cmdreg_tmp = cmdreg;
164                 //printf("WRITE: CMDREG to %02x\n", data);
165 #if defined(HAS_MB8866) || defined(HAS_MB8876)
166                 cmdreg = (~data) & 0xff;
167 #else
168                 cmdreg = data;
169 #endif
170                 process_cmd();
171                 no_command = 0;
172                 break;
173         case 1:
174                 // track reg
175                 //printf("WRITE: TRKREG to %02x\n", data);
176 #if defined(HAS_MB8866) || defined(HAS_MB8876)
177                 trkreg = (~data) & 0xff;
178 #else
179                 trkreg = data;
180 #endif
181                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
182                         // track reg is written after command starts
183                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
184                                 process_cmd();
185                         }
186                 }
187                 break;
188         case 2:
189                 // sector reg
190 #if defined(HAS_MB8866) || defined(HAS_MB8876)
191                 secreg = (~data) & 0xff;
192 #else
193                 secreg = data;
194 #endif
195                 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
196                         // sector reg is written after command starts
197                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
198                                 process_cmd();
199                         }
200                 }
201                 break;
202         case 3:
203                 // data reg
204 #if defined(HAS_MB8866) || defined(HAS_MB8876)
205                 datareg = (~data) & 0xff;
206 #else
207                 datareg = data;
208 #endif
209
210                 _stat = ((status & FDC_ST_DRQ) && !now_search);
211                 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
212                         _stat = (_stat && motor_on);
213                 }
214                 if(_stat) {
215                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
216                                 // write or multisector write
217                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
218                                         if(!disk[drvreg]->write_protected) {
219                                                 if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
220                                                         disk[drvreg]->sector[fdc[drvreg].index] = datareg;
221                                                         sector_changed = true;
222                                                 }
223                                                 // dm, ddm
224                                                 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
225                                         } else {
226                                                 status |= FDC_ST_WRITEFAULT;
227                                                 status &= ~FDC_ST_BUSY;
228                                                 cmdtype = 0;
229                                                 set_irq(true);
230                                         }
231                                         //fdc[drvreg].index++;
232                                 }
233                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
234                                         if(cmdtype == FDC_CMD_WR_SEC) {
235                                                 // single sector
236 #ifdef _FDC_DEBUG_LOG
237                                                 this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
238 #endif
239                                                 status &= ~FDC_ST_BUSY;
240                                                 cmdtype = 0;
241                                                 set_irq(true);
242                                         } else {
243                                                 // multisector
244 #ifdef _FDC_DEBUG_LOG
245                                                 this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
246 #endif
247                                                 // 2HD: 360rpm, 10410bytes/track -> 0.06246bytes/us
248                                                 register_my_event(EVENT_MULTI1, 30); // 0.06246bytes/us * 30us = 1.8738bytes < GAP3
249                                                 register_my_event(EVENT_MULTI2, 60); // 0.06246bytes/us * 60us = 3.7476bytes < GAP3
250                                         }
251                                         sector_changed = false;
252                                 } else if(status & FDC_ST_DRQ) {
253                                         if(fdc[drvreg].index == 0) {
254                                                 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
255                                         } else {
256                                                 register_drq_event(1);
257                                         }
258                                 }
259                                 status &= ~FDC_ST_DRQ;
260                         } else if(cmdtype == FDC_CMD_WR_TRK) {
261                                 // write track
262                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
263                                         if(!disk[drvreg]->write_protected) {
264                                                 if(fdc[drvreg].index == 0) {
265                                                         disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
266                                                         fdc[drvreg].id_written = false;
267                                                         fdc[drvreg].side = sidereg;
268                                                         fdc[drvreg].side_changed = false;
269                                                 }
270                                                 if(fdc[drvreg].side != sidereg) {
271                                                         fdc[drvreg].side_changed = true;
272                                                 }
273                                                 if(fdc[drvreg].side_changed) {
274                                                         // abort write track because disk side is changed
275                                                 } else if(datareg == 0xf5) {
276                                                         // write a1h in missing clock
277                                                 } else if(datareg == 0xf6) {
278                                                         // write c2h in missing clock
279                                                 } else if(datareg == 0xf7) {
280                                                         // write crc
281                                                         if(!fdc[drvreg].id_written) {
282                                                                 // insert new sector with data crc error
283 write_id:
284                                                                 uint8_t c = 0, h = 0, r = 0, n = 0;
285                                                                 fdc[drvreg].id_written = true;
286                                                                 fdc[drvreg].sector_found = false;
287                                                                 if(fdc[drvreg].index >= 4) {
288                                                                         c = disk[drvreg]->track[fdc[drvreg].index - 4];
289                                                                         h = disk[drvreg]->track[fdc[drvreg].index - 3];
290                                                                         r = disk[drvreg]->track[fdc[drvreg].index - 2];
291                                                                         n = disk[drvreg]->track[fdc[drvreg].index - 1];
292                                                                 }
293                                                                 fdc[drvreg].sector_length = 0x80 << (n & 3);
294                                                                 fdc[drvreg].sector_index = 0;
295                                                                 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
296                                                         } else if(fdc[drvreg].sector_found) {
297                                                                 // clear data crc error if all sector data are written
298                                                                 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
299                                                                         disk[drvreg]->set_data_crc_error(false);
300                                                                 }
301                                                                 fdc[drvreg].id_written = false;
302                                                         } else {
303                                                                 // data mark of current sector is not written
304                                                                 disk[drvreg]->set_data_mark_missing();
305                                                                 goto write_id;
306                                                         }
307                                                 } else if(fdc[drvreg].id_written) {
308                                                         if(fdc[drvreg].sector_found) {
309                                                                 // sector data
310                                                                 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
311                                                                         disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
312                                                                 }
313                                                                 fdc[drvreg].sector_index++;
314                                                         } else if(datareg == 0xf8 || datareg == 0xfb) {
315                                                                 // data mark
316                                                                 disk[drvreg]->set_deleted(datareg == 0xf8);
317                                                                 fdc[drvreg].sector_found = true;
318                                                         }
319                                                 }
320                                                 disk[drvreg]->track[fdc[drvreg].index] = datareg;
321                                         } else {
322                                                 status |= FDC_ST_WRITEFAULT;
323                                                 status &= ~FDC_ST_BUSY;
324                                                 status &= ~FDC_ST_DRQ;
325                                                 cmdtype = 0;
326                                                 set_irq(true);
327                                         }
328                                         //fdc[drvreg].index++;
329                                 }
330                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
331                                         if(!disk[drvreg]->write_protected) {
332                                                 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
333                                                         // data mark of last sector is not written
334                                                         disk[drvreg]->set_data_mark_missing();
335                                                 }
336                                                 disk[drvreg]->sync_buffer();
337                                         }
338                                         status &= ~FDC_ST_BUSY;
339                                         cmdtype = 0;
340                                         set_irq(true);
341                                 } else if(status & FDC_ST_DRQ) {
342                                         if(fdc[drvreg].index == 0) {
343                                                 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
344                                         } else {
345                                                 register_drq_event(1);
346                                         }
347                                 }
348                                 status &= ~FDC_ST_DRQ;
349                         }
350                         if(!(status & FDC_ST_DRQ)) {
351                                 cancel_my_event(EVENT_LOST);
352                                 set_drq(false);
353                                 fdc[drvreg].access = true;
354                         }
355                 }
356                 break;
357         }
358 }
359
360 uint32_t MB8877::read_io8(uint32_t addr)
361 {
362         uint32_t val;
363         bool _stat;
364         switch(addr & 3) {
365         case 0:
366                 // status reg
367                 if(now_search) {
368                         // now sector search
369                         val = FDC_ST_BUSY;
370                 } else {
371                         // disk not inserted, motor stop
372                         _stat = !disk[drvreg]->inserted;
373                         if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
374                                 _stat = (_stat || !motor_on);
375                         }
376                         if(_stat) {
377                                 status |= FDC_ST_NOTREADY;
378                         } else {
379                                 status &= ~FDC_ST_NOTREADY;
380                         }
381                         // write protected
382                         if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
383                                 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
384                                         status |= FDC_ST_WRITEP;
385                                 } else {
386                                         status &= ~FDC_ST_WRITEP;
387                                 }
388                         } else {
389                                 status &= ~FDC_ST_WRITEP;
390                         }
391                         // track0, index hole
392                         if(cmdtype == FDC_CMD_TYPE1) {
393                                 if(fdc[drvreg].track == 0) {
394                                         status |= FDC_ST_TRACK00;
395                                 } else {
396                                         status &= ~FDC_ST_TRACK00;
397                                 }
398                                 // index hole signal width is 5msec (thanks Mr.Sato)
399                                 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
400                                         status |= FDC_ST_INDEX;
401                                 } else {
402                                         status &= ~FDC_ST_INDEX;
403                                 }
404                         }
405                         // show busy a moment
406                         val = status;
407                         if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
408                                 status &= ~FDC_ST_BUSY;
409 #ifdef MB8877_NO_BUSY_AFTER_SEEK
410         #if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
411                                 if(disk[0]->is_special_disk != SPECIAL_DISK_FM7_XANADU2_D) {
412                                         val &= ~FDC_ST_BUSY;
413                                 }
414         #else                   
415                                 val &= ~FDC_ST_BUSY;
416         #endif
417 #endif
418                         }
419                 }
420                 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
421                         // MZ-2000 HuBASIC invites NOT READY status
422                         if(++no_command == 16) {
423                                 val |= FDC_ST_NOTREADY;
424                         }
425                 } else {
426                         no_command = 0;
427                 }
428                 // reset irq/drq
429                 if(!(status & FDC_ST_DRQ)) {
430                         set_drq(false);
431                 }
432                 if(!(status & FDC_ST_BUSY)) {
433                         set_irq(false);
434                 }
435
436 #ifdef _FDC_DEBUG_LOG
437                 this->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
438 #endif
439 #if defined(HAS_MB8866) || defined(HAS_MB8876)
440                 return (~val) & 0xff;
441 #else
442                 return val;
443 #endif
444         case 1:
445                 // track reg
446 #if defined(HAS_MB8866) || defined(HAS_MB8876)
447                 return (~trkreg) & 0xff;
448 #else
449                 return trkreg;
450 #endif
451         case 2:
452                 // sector reg
453 #if defined(HAS_MB8866) || defined(HAS_MB8876)
454                 return (~secreg) & 0xff;
455 #else
456                 return secreg;
457 #endif
458         case 3:
459                 // data reg
460                 _stat = ((status & FDC_ST_DRQ) && !now_search);
461                 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
462                         _stat = (_stat && motor_on);
463                 }
464                 if(_stat) {
465                 //if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
466                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
467                                 // read or multisector read
468                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
469                                         datareg = disk[drvreg]->sector[fdc[drvreg].index];
470                                         //fdc[drvreg].index++;
471                                 }
472                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
473
474                                         if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
475                                                 // data crc error
476 #ifdef _FDC_DEBUG_LOG
477                                                 this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
478 #endif
479                                                 status |= FDC_ST_CRCERR;
480                                                 status &= ~FDC_ST_BUSY;
481                                                 cmdtype = 0;
482                                                 set_irq(true);
483                                         } else if(cmdtype == FDC_CMD_RD_SEC) {
484                                                 // single sector
485 #ifdef _FDC_DEBUG_LOG
486                                                 this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
487 #endif
488                                                 status &= ~FDC_ST_BUSY;
489                                                 cmdtype = 0;
490                                                 set_irq(true);
491                                         } else {
492                                                 // multisector
493 #ifdef _FDC_DEBUG_LOG
494                                                 this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
495 #endif
496                                                 register_my_event(EVENT_MULTI1, 30);
497                                                 register_my_event(EVENT_MULTI2, 60);
498                                         }
499                                 } else {
500                                         register_drq_event(1);
501                                 }
502                                 status &= ~FDC_ST_DRQ;
503                         } else if(cmdtype == FDC_CMD_RD_ADDR) {
504                                 // read address
505                                 if(fdc[drvreg].index < 6) {
506                                         datareg = disk[drvreg]->id[fdc[drvreg].index];
507                                         //fdc[drvreg].index++;
508                                 }
509                                 if((fdc[drvreg].index + 1) >= 6) {
510                                         if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
511                                                 // id crc error
512                                                 status |= FDC_ST_CRCERR;
513                                         }
514                                         status &= ~FDC_ST_BUSY;
515                                         cmdtype = 0;
516                                         set_irq(true);
517 #ifdef _FDC_DEBUG_LOG
518                                         this->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
519 #endif
520                                 } else {
521                                         register_drq_event(1);
522                                 }
523                                 status &= ~FDC_ST_DRQ;
524                         } else if(cmdtype == FDC_CMD_RD_TRK) {
525                                 // read track
526                                 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
527                                         datareg = disk[drvreg]->track[fdc[drvreg].index];
528                                         //fdc[drvreg].index++;
529                                 }
530                                 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
531 #ifdef _FDC_DEBUG_LOG
532                                         this->out_debug_log(_T("FDC\tEND OF TRACK\n"));
533 #endif
534                                         status &= ~FDC_ST_BUSY;
535                                         status |= FDC_ST_LOSTDATA;
536                                         cmdtype = 0;
537                                         set_irq(true);
538                                 } else {
539                                         register_drq_event(1);
540                                 }
541                                 status &= ~FDC_ST_DRQ;
542                         }
543                         if(!(status & FDC_ST_DRQ)) {
544                                 cancel_my_event(EVENT_LOST);
545                                 set_drq(false);
546                                 fdc[drvreg].access = true;
547                         }
548                 }
549 #ifdef _FDC_DEBUG_LOG
550                 this->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
551 #endif
552 #if defined(HAS_MB8866) || defined(HAS_MB8876)
553                 return (~datareg) & 0xff;
554 #else
555                 return datareg;
556 #endif
557         }
558         return 0xff;
559 }
560
561 void MB8877::write_dma_io8(uint32_t addr, uint32_t data)
562 {
563         write_io8(3, data);
564 }
565
566 uint32_t MB8877::read_dma_io8(uint32_t addr)
567 {
568         return read_io8(3);
569 }
570
571 void MB8877::write_signal(int id, uint32_t data, uint32_t mask)
572 {
573         if(id == SIG_MB8877_DRIVEREG) {
574                 drvreg = data & DRIVE_MASK;
575                 drive_sel = true;
576                 seekend_clock = get_current_clock();
577         } else if(id == SIG_MB8877_SIDEREG) {
578                 sidereg = (data & mask) ? 1 : 0;
579         } else if(id == SIG_MB8877_MOTOR) {
580                 motor_on = ((data & mask) != 0);
581         }
582 #if defined(USE_SOUND_FILES)
583         else if((id >= SIG_SOUNDER_MUTE) && (id < (SIG_SOUNDER_MUTE + 2))) {
584                 snd_mute = ((data & mask) != 0);
585         } else if((id >= SIG_SOUNDER_RELOAD) && (id < (SIG_SOUNDER_RELOAD + 2))) {
586                 reload_sound_data(id - SIG_SOUNDER_RELOAD);
587         }
588 #endif
589 }
590
591 uint32_t MB8877::read_signal(int ch)
592 {
593         // get access status
594         uint32_t stat = 0;
595         if(ch == SIG_MB8877_DRIVEREG) {
596                 return drvreg & DRIVE_MASK;
597         } else if(ch == SIG_MB8877_SIDEREG) {
598                 return sidereg & 1;
599         } else if(ch == SIG_MB8877_MOTOR) {
600                 return motor_on ? 1 : 0;
601         }
602         for(int i = 0; i < MAX_DRIVE; i++) {
603                 if(fdc[i].access) {
604                         stat |= 1 << i;
605                 }
606                 fdc[i].access = false;
607         }
608         if(now_search) {
609                 stat |= 1 << drvreg;
610         }
611         return stat;
612 }
613
614 void MB8877::event_callback(int event_id, int err)
615 {
616         int event = event_id >> 8;
617         int cmd = event_id & 0xff;
618         register_id[event] = -1;
619         
620         // cancel event if the command is finished or other command is executed
621         if(cmd != cmdtype) {
622                 if(event == EVENT_SEEK || event == EVENT_SEEKEND) {
623                         now_seek = false;
624                 } else if(event == EVENT_SEARCH) {
625                         now_search = false;
626                 }
627                 return;
628         }
629         switch(event) {
630         case EVENT_SEEK:
631 #ifdef _FDC_DEBUG_LOG
632                 //this->out_debug_log(_T("FDC\tSEEK START\n"));
633 #endif
634                 if(seektrk > fdc[drvreg].track) {
635                         fdc[drvreg].track++;
636 #if defined(USE_SOUND_FILES)
637                         add_sound(MB8877_SND_TYPE_SEEK);
638 #endif                  
639                 } else if(seektrk < fdc[drvreg].track) {
640                         fdc[drvreg].track--;
641 #if defined(USE_SOUND_FILES)
642                         add_sound(MB8877_SND_TYPE_SEEK);
643 #endif                  
644                 }
645                 //printf("TRK: %d\n", fdc[drvreg].track);
646                 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
647                         trkreg = fdc[drvreg].track;
648                 }
649                 if(seektrk != fdc[drvreg].track) {
650                         register_seek_event();
651                         break;
652                 }
653                 seekend_clock = get_current_clock();
654 #ifdef HAS_MB89311
655                 if(extended_mode) {
656                         if((cmdreg & 0xf4) == 0x44) {
657                                 // read-after-seek
658                                 cmd_readdata(true);
659                                 break;
660                         } else if((cmdreg & 0xf4) == 0x64) {
661                                 // write-after-seek
662                                 cmd_writedata(true);
663                                 break;
664                         }
665                 }
666 #endif
667                 status_tmp = status;
668                 if(cmdreg & 4) {
669                         // verify
670                         status_tmp |= search_track();
671                         double time;
672                         if(status_tmp & FDC_ST_SEEKERR) {
673                                 time = get_usec_to_detect_index_hole(5, true);
674                         } else {
675                                 time = get_usec_to_next_trans_pos(true);
676                         }
677                         register_my_event(EVENT_SEEKEND, time);
678                         break;
679                 }
680         case EVENT_SEEKEND:
681                 now_seek = false;
682                 status = status_tmp;
683                 set_irq(true);
684 #ifdef _FDC_DEBUG_LOG
685                 //this->out_debug_log(_T("FDC\tSEEK END\n"));
686 #endif
687                 break;
688         case EVENT_SEARCH:
689                 now_search = false;
690                 if(status_tmp & FDC_ST_RECNFND) {
691 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
692                         // for SHARP X1 Batten Tanuki
693                         if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
694                                 status_tmp &= ~FDC_ST_RECNFND;
695                         }
696 #endif
697 #ifdef _FDC_DEBUG_LOG
698                         this->out_debug_log(_T("FDC\tSEARCH NG\n"));
699 #endif
700                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
701                         cmdtype = 0;
702                         set_irq(true);
703                 } else if(status_tmp & FDC_ST_WRITEFAULT) {
704 #ifdef _FDC_DEBUG_LOG
705                         this->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
706 #endif
707                         status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
708                         cmdtype = 0;
709                         set_irq(true);
710                 } else {
711                         status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
712                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
713                                 register_lost_event(8);
714                         } else if(cmdtype == FDC_CMD_WR_TRK) {
715                                 register_lost_event(3);
716                         } else {
717                                 register_lost_event(1);
718                         }
719                         fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
720                         fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
721                         set_drq(true);
722                         drive_sel = false;
723 #ifdef _FDC_DEBUG_LOG
724                         this->out_debug_log(_T("FDC\tSEARCH OK\n"));
725 #endif
726                 }
727                 break;
728         case EVENT_DRQ:
729                 if(status & FDC_ST_BUSY) {
730                         status |= FDC_ST_DRQ;
731                         register_lost_event(1);
732                         if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
733                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
734                         } else {
735                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
736                         }
737                         if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
738                            cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
739                            cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK  ||
740                            cmdtype == FDC_CMD_RD_ADDR) {
741                                 fdc[drvreg].index++;
742                         }
743                         fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
744                         set_drq(true);
745 #ifdef _FDC_DEBUG_LOG
746                         //this->out_debug_log(_T("FDC\tDRQ!\n"));
747 #endif
748                 }
749                 break;
750         case EVENT_MULTI1:
751                 secreg++;
752                 break;
753         case EVENT_MULTI2:
754                 if(cmdtype == FDC_CMD_RD_MSEC) {
755                         cmd_readdata(false);
756                 } else if(cmdtype == FDC_CMD_WR_MSEC) {
757                         cmd_writedata(false);
758                 }
759                 break;
760         case EVENT_LOST:
761                 if(status & FDC_ST_BUSY) {
762 #ifdef _FDC_DEBUG_LOG
763                         this->out_debug_log(_T("FDC\tDATA LOST\n"));
764 #endif
765                         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
766                                 if(fdc[drvreg].index == 0) {
767                                         status &= ~FDC_ST_BUSY;
768                                         //status &= ~FDC_ST_DRQ;
769                                         cmdtype = 0;
770                                         set_irq(true);
771                                         //set_drq(false);
772                                 } else {
773                                         write_io8(3, 0x00);
774                                 }
775                         } else {
776                                 read_io8(3);
777                         }
778                         status |= FDC_ST_LOSTDATA;
779                 }
780                 break;
781         }
782 }
783
784 // ----------------------------------------------------------------------------
785 // command
786 // ----------------------------------------------------------------------------
787
788 void MB8877::process_cmd()
789 {
790         set_irq(false);
791         set_drq(false);
792         
793 #ifdef HAS_MB89311
794         // MB89311 mode commands
795         if(cmdreg == 0xfc) {
796                 // delay (may be only in extended mode)
797                 #ifdef _FDC_DEBUG_LOG
798                         this->out_debug_log(_T("FDC\tCMD=%2xh (DELAY   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
799                 #endif
800                 cmdtype = status = 0;
801                 return;
802         } else if(cmdreg == 0xfd) {
803                 // assign parameter
804                 #ifdef _FDC_DEBUG_LOG
805                         this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN PAR) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
806                 #endif
807                 cmdtype = status = 0;
808                 return;
809         } else if(cmdreg == 0xfe) {
810                 // assign mode
811                 #ifdef _FDC_DEBUG_LOG
812                         this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN MOD) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
813                 #endif
814                 extended_mode = !extended_mode;
815                 cmdtype = status = 0;
816                 return;
817         } else if(cmdreg == 0xff) {
818                 // reset (may be only in extended mode)
819                 #ifdef _FDC_DEBUG_LOG
820                         this->out_debug_log(_T("FDC\tCMD=%2xh (RESET   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
821                 #endif
822                 cmd_forceint();
823                 extended_mode = true;
824                 return;
825         } else if(extended_mode) {
826                 // type-1
827                 if((cmdreg & 0xeb) == 0x21) {
828                         #ifdef _FDC_DEBUG_LOG
829                                 this->out_debug_log(_T("FDC\tCMD=%2xh (STEP IN ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
830                         #endif
831                         cmd_stepin();
832                         return;
833                 } else if((cmdreg & 0xeb) == 0x22) {
834                         #ifdef _FDC_DEBUG_LOG
835                                 this->out_debug_log(_T("FDC\tCMD=%2xh (STEP OUT) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
836                         #endif
837                         cmd_stepout();
838                         return;
839                 // type-2
840                 } else if((cmdreg & 0xf4) == 0x44) {
841                         // read-after-seek
842                         #ifdef _FDC_DEBUG_LOG
843                                 this->out_debug_log(_T("FDC\tCMD=%2xh (RDaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
844                         #endif
845                         cmd_seek();
846                         return;
847                 } else if((cmdreg & 0xf4) == 0x64) {
848                         // write-after-seek
849                         #ifdef _FDC_DEBUG_LOG
850                                 this->out_debug_log(_T("FDC\tCMD=%2xh (WRaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
851                         #endif
852                         cmd_seek();
853                         return;
854                 // type-3
855                 } else if((cmdreg & 0xfb) == 0xf1) {
856                         // format
857                         #ifdef _FDC_DEBUG_LOG
858                                 this->out_debug_log(_T("FDC\tCMD=%2xh (FORMAT  ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
859                         #endif
860                         cmd_format();
861                         return;
862                 }
863         }
864 #endif
865         
866         // MB8877 mode commands
867 #ifdef _FDC_DEBUG_LOG
868         static const _TCHAR *cmdstr[0x10] = {
869                 _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),
870                 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
871                 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
872                 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
873         };
874         this->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);
875 #endif
876         
877         switch(cmdreg & 0xf8) {
878         // type-1
879         case 0x00: case 0x08:
880                 cmd_restore();
881                 break;
882         case 0x10: case 0x18:
883                 cmd_seek();
884                 break;
885         case 0x20: case 0x28:
886         case 0x30: case 0x38:
887                 cmd_step();
888                 break;
889         case 0x40: case 0x48:
890         case 0x50: case 0x58:
891                 cmd_stepin();
892                 break;
893         case 0x60: case 0x68:
894         case 0x70: case 0x78:
895                 cmd_stepout();
896                 break;
897         // type-2
898         case 0x80: case 0x88:
899         case 0x90: case 0x98:
900                 cmd_readdata(true);
901                 break;
902         case 0xa0:case 0xa8:
903         case 0xb0: case 0xb8:
904                 cmd_writedata(true);
905                 break;
906         // type-3
907         case 0xc0:
908                 cmd_readaddr();
909                 break;
910         case 0xe0:
911                 cmd_readtrack();
912                 break;
913         case 0xf0:
914                 cmd_writetrack();
915                 break;
916         // type-4
917         case 0xd0: case 0xd8:
918                 cmd_forceint();
919                 break;
920         // unknown command
921         default:
922                 break;
923         }
924 }
925
926 void MB8877::cmd_restore()
927 {
928         // type-1 restore
929         cmdtype = FDC_CMD_TYPE1;
930         status = FDC_ST_HEADENG | FDC_ST_BUSY;
931         trkreg = 0xff;
932         datareg = 0;
933         
934         seektrk = 0;
935         seekvct = true;
936         
937         register_seek_event();
938 }
939
940 void MB8877::cmd_seek()
941 {
942         // type-1 seek
943         cmdtype = FDC_CMD_TYPE1;
944         status = FDC_ST_HEADENG | FDC_ST_BUSY;
945         
946         seektrk = datareg;
947         //printf("SEEK %d -> %d\n", trkreg, seektrk);
948 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
949         defined(_FM77AV20) || defined(_FM77AV20EX)
950         if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
951                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
952         } else {
953                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
954         }
955 #else
956         if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
957                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
958         } else {
959                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
960         }
961 #endif
962 //      seekvct = !(datareg > trkreg);
963         seekvct = !(seektrk > fdc[drvreg].track);       
964         if(cmdreg & 4) {
965                 // verify
966                 if(trkreg != fdc[drvreg].track) {
967                         status |= FDC_ST_SEEKERR;
968                         trkreg = fdc[drvreg].track;
969                 }
970         }
971         trkreg  = datareg;
972         register_seek_event();
973 }
974
975 void MB8877::cmd_step()
976 {
977         // type-1 step
978         if(seekvct) {
979                 cmd_stepout();
980         } else {
981                 cmd_stepin();
982         }
983 }
984
985 void MB8877::cmd_stepin()
986 {
987         // type-1 step in
988         cmdtype = FDC_CMD_TYPE1;
989         status = FDC_ST_HEADENG | FDC_ST_BUSY;
990         
991         seektrk = fdc[drvreg].track + 1;
992 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
993         defined(_FM77AV20) || defined(_FM77AV20EX)
994         if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
995                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
996         } else {
997                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
998         }
999 #else
1000         if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
1001                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
1002         } else {
1003                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
1004         }
1005 #endif
1006         seekvct = false;
1007         if(cmdreg & 4) {
1008                 // verify
1009                 if(trkreg != fdc[drvreg].track) {
1010                         status |= FDC_ST_SEEKERR;
1011                         //trkreg = fdc[drvreg].track;
1012                 }
1013         }
1014         register_seek_event();
1015 }
1016
1017 void MB8877::cmd_stepout()
1018 {
1019         // type-1 step out
1020         cmdtype = FDC_CMD_TYPE1;
1021         status = FDC_ST_HEADENG | FDC_ST_BUSY;
1022         
1023         seektrk = fdc[drvreg].track - 1;
1024 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
1025         defined(_FM77AV20) || defined(_FM77AV20EX)
1026         if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
1027                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
1028         } else {
1029                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
1030         }
1031 #else
1032         if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
1033                 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
1034         } else {
1035                 seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
1036         }
1037 #endif
1038         seekvct = true;
1039         if(cmdreg & 4) {
1040                 // verify
1041                 if(trkreg != fdc[drvreg].track) {
1042                         status |= FDC_ST_SEEKERR;
1043                         //trkreg = fdc[drvreg].track;
1044                 }
1045         }
1046         register_seek_event();
1047 }
1048
1049 void MB8877::cmd_readdata(bool first_sector)
1050 {
1051         // type-2 read data
1052         cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
1053         status = FDC_ST_BUSY;
1054         status_tmp = search_sector();
1055         now_search = true;
1056         
1057         double time;
1058         if(status_tmp & FDC_ST_RECNFND) {
1059                 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1060         } else {
1061                 time = get_usec_to_start_trans(first_sector);
1062         }
1063         register_my_event(EVENT_SEARCH, time);
1064         cancel_my_event(EVENT_LOST);
1065 }
1066
1067 void MB8877::cmd_writedata(bool first_sector)
1068 {
1069         // type-2 write data
1070         cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
1071         status = FDC_ST_BUSY;
1072         status_tmp = search_sector() & ~FDC_ST_RECTYPE;
1073         now_search = true;
1074         sector_changed = false;
1075         
1076         double time;
1077         if(status_tmp & FDC_ST_RECNFND) {
1078                 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1079         } else if(status & FDC_ST_WRITEFAULT) {
1080                 time = (cmdreg & 4) ? DELAY_TIME : 1;
1081         } else {
1082                 time = get_usec_to_start_trans(first_sector);
1083         }
1084         register_my_event(EVENT_SEARCH, time);
1085         cancel_my_event(EVENT_LOST);
1086 }
1087
1088 void MB8877::cmd_readaddr()
1089 {
1090         // type-3 read address
1091         cmdtype = FDC_CMD_RD_ADDR;
1092         status = FDC_ST_BUSY;
1093         status_tmp = search_addr();
1094         now_search = true;
1095         
1096         double time;
1097         if(status_tmp & FDC_ST_RECNFND) {
1098                 time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
1099         } else {
1100                 time = get_usec_to_start_trans(true);
1101         }
1102         register_my_event(EVENT_SEARCH, time);
1103         cancel_my_event(EVENT_LOST);
1104 }
1105
1106 void MB8877::cmd_readtrack()
1107 {
1108         // type-3 read track
1109         cmdtype = FDC_CMD_RD_TRK;
1110         status = FDC_ST_BUSY;
1111         status_tmp = 0;
1112         
1113         disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1114         fdc[drvreg].index = 0;
1115         now_search = true;
1116         
1117         fdc[drvreg].next_trans_position = 1;
1118         double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
1119         register_my_event(EVENT_SEARCH, time);
1120         cancel_my_event(EVENT_LOST);
1121 }
1122
1123 void MB8877::cmd_writetrack()
1124 {
1125         // type-3 write track
1126         cmdtype = FDC_CMD_WR_TRK;
1127         status = FDC_ST_BUSY;
1128         status_tmp = 0;
1129         
1130         fdc[drvreg].index = 0;
1131         fdc[drvreg].id_written = false;
1132         now_search = true;
1133         
1134         double time;
1135         if(disk[drvreg]->write_protected) {
1136                 status_tmp = FDC_ST_WRITEFAULT;
1137                 time = (cmdreg & 4) ? DELAY_TIME : 1;
1138         } else {
1139                 if(cmdreg & 4) {
1140                         // wait 15msec before raise first drq
1141                         fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1142                         time = DELAY_TIME;
1143                 } else {
1144                         // raise first drq soon
1145                         fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1146                         time = disk[drvreg]->get_usec_per_bytes(1);
1147                 }
1148                 // wait at least 3bytes before check index hole and raise second drq
1149                 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1150                 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1151                         fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1152                 }
1153         }
1154         register_my_event(EVENT_SEARCH, time);
1155         cancel_my_event(EVENT_LOST);
1156 }
1157
1158 #ifdef HAS_MB89311
1159 void MB8877::cmd_format()
1160 {
1161         // type-3 format (FIXME: need to implement)
1162         cmdtype = FDC_CMD_WR_TRK;
1163         status = FDC_ST_BUSY;
1164         status_tmp = 0;
1165         
1166         fdc[drvreg].index = 0;
1167         fdc[drvreg].id_written = false;
1168         now_search = true;
1169         
1170         status_tmp = FDC_ST_WRITEFAULT;
1171         double time = (cmdreg & 4) ? DELAY_TIME : 1;
1172         
1173         register_my_event(EVENT_SEARCH, time);
1174         cancel_my_event(EVENT_LOST);
1175 }
1176 #endif
1177
1178 void MB8877::cmd_forceint()
1179 {
1180         // type-4 force interrupt
1181         if(cmdtype == FDC_CMD_TYPE1) {
1182                 // abort restore/seek/step command
1183                 if(now_seek) {
1184                         if(seektrk > fdc[drvreg].track) {
1185                                 fdc[drvreg].track++;
1186                         } else if(seektrk < fdc[drvreg].track) {
1187                                 fdc[drvreg].track--;
1188                         }
1189                         if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
1190                                 trkreg = fdc[drvreg].track;
1191                         }
1192                 }
1193         } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1194                 // abort write sector command
1195                 if(sector_changed) {
1196                         disk[drvreg]->set_data_crc_error(false);
1197                 }
1198         } else if(cmdtype == FDC_CMD_WR_TRK) {
1199                 // abort write track command
1200                 if(!disk[drvreg]->write_protected) {
1201                         if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1202                                 // data mark of last sector is not written
1203                                 disk[drvreg]->set_data_mark_missing();
1204                         }
1205                         disk[drvreg]->sync_buffer();
1206                 }
1207         }
1208         now_search = now_seek = sector_changed = false;
1209         
1210 #ifdef HAS_MB89311
1211         if(cmdreg == 0xff) {
1212                 // reset command
1213                 cmdtype = FDC_CMD_TYPE1;
1214                 status = FDC_ST_HEADENG;
1215         } else {
1216 #endif
1217                 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1218                         cmdtype = FDC_CMD_TYPE1;
1219                         status = FDC_ST_HEADENG;
1220                 }
1221                 status &= ~FDC_ST_BUSY;
1222                 
1223                 // force interrupt if bit0-bit3 is high
1224                 if(cmdreg & 0x0f) {
1225                         set_irq(true);
1226                 }
1227 #ifdef HAS_MB89311
1228         }
1229 #endif
1230         
1231         cancel_my_event(EVENT_SEEK);
1232         cancel_my_event(EVENT_SEEKEND);
1233         cancel_my_event(EVENT_SEARCH);
1234         cancel_my_event(EVENT_DRQ);
1235         cancel_my_event(EVENT_MULTI1);
1236         cancel_my_event(EVENT_MULTI2);
1237         cancel_my_event(EVENT_LOST);
1238 }
1239
1240 // ----------------------------------------------------------------------------
1241 // media handler
1242 // ----------------------------------------------------------------------------
1243
1244 uint8_t MB8877::search_track()
1245 {
1246         // get track
1247         int track = fdc[drvreg].track;
1248 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
1249         defined(_FM77AV20) || defined(_FM77AV20EX)
1250         if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
1251                 if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
1252                    (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
1253                    (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
1254                         track >>= 1;
1255                 }
1256         }
1257 #endif
1258         if(!disk[drvreg]->get_track(track, sidereg)){
1259                 return FDC_ST_SEEKERR;
1260         }
1261         
1262         // verify track number
1263         if(disk[drvreg]->ignore_crc()) {
1264                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1265                         disk[drvreg]->get_sector(-1, -1, i);
1266                         if(disk[drvreg]->id[0] == trkreg) {
1267                                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1268                                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1269                                 return 0;
1270                         }
1271                 }
1272         } else {
1273                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1274                         disk[drvreg]->get_sector(-1, -1, i);
1275                         if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1276                                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1277                                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1278                                 return 0;
1279                         }
1280                 }
1281                 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1282                         disk[drvreg]->get_sector(-1, -1, i);
1283                         if(disk[drvreg]->id[0] == trkreg) {
1284                                 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1285                         }
1286                 }
1287         }
1288         return FDC_ST_SEEKERR;
1289 }
1290
1291 uint8_t MB8877::search_sector()
1292 {
1293         // write protect
1294         if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1295                 if(disk[drvreg]->write_protected) {
1296                         return FDC_ST_WRITEFAULT;
1297                 }
1298         }
1299         
1300         // get track
1301         int track = fdc[drvreg].track;
1302 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
1303         defined(_FM77AV20) || defined(_FM77AV20EX)
1304         if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
1305                 if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
1306                    (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
1307                    (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
1308                         track >>= 1;
1309                 }
1310         }
1311 #endif
1312         if(!disk[drvreg]->get_track(track, sidereg)) {
1313                 return FDC_ST_RECNFND;
1314         }
1315         
1316         // get current position
1317         int sector_num = disk[drvreg]->sector_num.sd;
1318         int position = get_cur_position();
1319         
1320         if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1321                 position -= disk[drvreg]->get_track_size();
1322         }
1323         
1324         // first scanned sector
1325         int first_sector = 0;
1326         for(int i = 0; i < sector_num; i++) {
1327                 if(position < disk[drvreg]->am1_position[i]) {
1328                         first_sector = i;
1329                         break;
1330                 }
1331         }
1332         
1333         // scan sectors
1334         for(int i = 0; i < sector_num; i++) {
1335                 // get sector
1336                 int index = (first_sector + i) % sector_num;
1337                 disk[drvreg]->get_sector(-1, -1, index);
1338                 //printf("CHRN=%02x %02x %02x %02x\n", disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3]);
1339                 // check id
1340                 if(disk[drvreg]->id[0] != trkreg) {
1341                         continue;
1342                 }
1343 #if !defined(HAS_MB8866)
1344                 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1345                         continue;
1346                 }
1347 #endif
1348                 if(disk[drvreg]->id[2] != secreg) {
1349                         continue;
1350                 }
1351                 if(disk[drvreg]->sector_size.sd == 0) {
1352                         continue;
1353                 }
1354                 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1355                         // id crc error
1356                         disk[drvreg]->sector_size.sd = 0;
1357                         return FDC_ST_RECNFND | FDC_ST_CRCERR;
1358                 }
1359                 
1360                 // sector found
1361                 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1362                         fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
1363                         fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
1364                 } else {
1365                         fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
1366                 }
1367                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[index];
1368                 fdc[drvreg].index = 0;
1369 #ifdef _FDC_DEBUG_LOG
1370                 this->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1371                         disk[drvreg]->sector_size.sd,
1372                         disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1373                         disk[drvreg]->id[4], disk[drvreg]->id[5],
1374                         disk[drvreg]->data_crc_error ? 1 : 0);
1375 #endif
1376                 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1377         }
1378         
1379         // sector not found
1380         disk[drvreg]->sector_size.sd = 0;
1381         return FDC_ST_RECNFND;
1382 }
1383
1384 uint8_t MB8877::search_addr()
1385 {
1386         // get track
1387         int track = fdc[drvreg].track;
1388 #if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || \
1389         defined(_FM77AV20) || defined(_FM77AV20EX)
1390         if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
1391                 if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
1392                    (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
1393                    (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
1394                         track >>= 1;
1395                 }
1396         }
1397 #endif
1398         if(!disk[drvreg]->get_track(track, sidereg)) {
1399                 return FDC_ST_RECNFND;
1400         }
1401         
1402         // get current position
1403         int sector_num = disk[drvreg]->sector_num.sd;
1404         int position = get_cur_position();
1405         
1406         if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1407                 position -= disk[drvreg]->get_track_size();
1408         }
1409         
1410         // first scanned sector
1411         int first_sector = 0;
1412         for(int i = 0; i < sector_num; i++) {
1413                 if(position < disk[drvreg]->am1_position[i]) {
1414                         first_sector = i;
1415                         break;
1416                 }
1417         }
1418         
1419         // get sector
1420         if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1421                 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
1422                 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
1423                 fdc[drvreg].index = 0;
1424                 secreg = disk[drvreg]->id[0];
1425                 return 0;
1426         }
1427         
1428         // sector not found
1429         disk[drvreg]->sector_size.sd = 0;
1430         return FDC_ST_RECNFND;
1431 }
1432
1433 // ----------------------------------------------------------------------------
1434 // timing
1435 // ----------------------------------------------------------------------------
1436
1437 int MB8877::get_cur_position()
1438 {
1439         return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1440 }
1441
1442 double MB8877::get_usec_to_start_trans(bool first_sector)
1443 {
1444         // get time from current position
1445         double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
1446         if(first_sector && time < 60000 - get_passed_usec(seekend_clock)) {
1447                 time += disk[drvreg]->get_usec_per_track();
1448         }
1449         return time;
1450 }
1451
1452 double MB8877::get_usec_to_next_trans_pos(bool delay)
1453 {
1454         int position = get_cur_position();
1455         
1456         if(disk[drvreg]->invalid_format) {
1457                 // XXX: this track is invalid format and the calculated sector position may be incorrect.
1458                 // so use the constant period
1459                 return 50000;
1460         } else if(/*disk[drvreg]->no_skew && */!disk[drvreg]->correct_timing()) {
1461                 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1462                 // so use the period to search the next sector from the current position
1463                 int sector_num = disk[drvreg]->sector_num.sd;
1464                 int bytes = -1;
1465                 
1466                 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1467                         position -= disk[drvreg]->get_track_size();
1468                 }
1469                 for(int i = 0; i < sector_num; i++) {
1470                         if(position < disk[drvreg]->am1_position[i]) {
1471                                 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1472                                         bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
1473                                 } else {
1474                                         bytes = (disk[drvreg]->data_position[i] + 1) - position;
1475                                 }
1476                                 if(bytes < 0) {
1477                                         bytes += disk[drvreg]->get_track_size(); // to make sure
1478                                 }
1479                                 break;
1480                         }
1481                 }
1482                 if(bytes > 0) {
1483                         return disk[drvreg]->get_usec_per_bytes(bytes);
1484                 }
1485                 return 50000;
1486         }
1487         if(delay) {
1488                 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1489         }
1490         int bytes = fdc[drvreg].next_trans_position - position;
1491         if(fdc[drvreg].next_am1_position < position || bytes < 0) {
1492                 bytes += disk[drvreg]->get_track_size();
1493         }
1494         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1495         if(delay) {
1496                 time += DELAY_TIME;
1497         }
1498         return time;
1499 }
1500
1501 double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
1502 {
1503         int position = get_cur_position();
1504         if(delay) {
1505                 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1506         }
1507         int bytes = disk[drvreg]->get_track_size() * count - position;
1508         if(bytes < 0) {
1509                 bytes += disk[drvreg]->get_track_size();
1510         }
1511         double time = disk[drvreg]->get_usec_per_bytes(bytes);
1512         if(delay) {
1513                 time += DELAY_TIME;
1514         }
1515         return time;
1516 }
1517
1518 // ----------------------------------------------------------------------------
1519 // irq / drq
1520 // ----------------------------------------------------------------------------
1521
1522 void MB8877::set_irq(bool val)
1523 {
1524         write_signals(&outputs_irq, val ? 0xffffffff : 0);
1525 }
1526
1527 void MB8877::set_drq(bool val)
1528 {
1529         write_signals(&outputs_drq, val ? 0xffffffff : 0);
1530 }
1531
1532 // ----------------------------------------------------------------------------
1533 // user interface
1534 // ----------------------------------------------------------------------------
1535
1536 void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
1537 {
1538         if(drv < MAX_DRIVE) {
1539                 disk[drv]->open(file_path, bank);
1540         }
1541 }
1542
1543 void MB8877::close_disk(int drv)
1544 {
1545         if(drv < MAX_DRIVE) {
1546                 disk[drv]->close();
1547                 cmdtype = 0;
1548         }
1549 }
1550
1551 bool MB8877::is_disk_inserted(int drv)
1552 {
1553         if(drv < MAX_DRIVE) {
1554                 return disk[drv]->inserted;
1555         }
1556         return false;
1557 }
1558
1559 void MB8877::is_disk_protected(int drv, bool value)
1560 {
1561         if(drv < MAX_DRIVE) {
1562                 disk[drv]->write_protected = value;
1563         }
1564 }
1565
1566 bool MB8877::is_disk_protected(int drv)
1567 {
1568         if(drv < MAX_DRIVE) {
1569                 return disk[drv]->write_protected;
1570         }
1571         return false;
1572 }
1573
1574 void MB8877::set_drive_type(int drv, uint8_t type)
1575 {
1576         if(drv < MAX_DRIVE) {
1577                 disk[drv]->drive_type = type;
1578         }
1579 }
1580
1581 uint8_t MB8877::get_drive_type(int drv)
1582 {
1583         if(drv < MAX_DRIVE) {
1584                 return disk[drv]->drive_type;
1585         }
1586         return DRIVE_TYPE_UNK;
1587 }
1588
1589 void MB8877::set_drive_rpm(int drv, int rpm)
1590 {
1591         if(drv < MAX_DRIVE) {
1592                 disk[drv]->drive_rpm = rpm;
1593         }
1594 }
1595
1596 void MB8877::set_drive_mfm(int drv, bool mfm)
1597 {
1598         if(drv < MAX_DRIVE) {
1599                 disk[drv]->drive_mfm = mfm;
1600         }
1601 }
1602
1603 uint8_t MB8877::fdc_status()
1604 {
1605         // for each virtual machines
1606 #if defined(_FMR50) || defined(_FMR60)
1607         return disk[drvreg]->inserted ? 2 : 0;
1608 #else
1609         return 0;
1610 #endif
1611 }
1612
1613 // Set sound data.
1614 // TYPE=
1615 //     0: FDD SEEK
1616 //     1: HEAD ENGAGE (Optional?)
1617 #if defined(USE_SOUND_FILES)
1618 void MB8877::add_sound(int type)
1619 {
1620         int *p;
1621         if(type == MB8877_SND_TYPE_SEEK) {
1622                 p = snd_seek_mix_tbl;
1623         } else if(type == MB8877_SND_TYPE_HEAD) {
1624                 p = snd_head_mix_tbl;
1625         } else {
1626                 return;
1627         }
1628         touch_sound();
1629         for(int i = 0; i < MB8877_SND_TBL_MAX; i++) {
1630                 if(p[i] < 0) {
1631                         p[i] = 0;
1632                         break;
1633                 }
1634         }
1635 }
1636
1637 bool MB8877::load_sound_data(int type, const _TCHAR *pathname)
1638 {
1639         if((type < 0) || (type > 1)) return false;
1640         int16_t *data = NULL;
1641         int dst_size = 0;
1642         int id = (this_device_id << 8) + type;
1643         const _TCHAR *sp;
1644         sp = create_local_path(pathname);
1645         emu->load_sound_file(id, sp, &data, &dst_size);
1646         if((dst_size <= 0) || (data == NULL)) { // Failed
1647                 this->out_debug_log("ID=%d : Failed to load SOUND FILE for %s:%s", id, (type == 0) ? _T("SEEK") : _T("HEAD") ,pathname);
1648                 return false;
1649         } else {
1650                 int utl_size = dst_size * 2 * sizeof(int16_t);
1651                 int alloc_size = utl_size + 64;
1652                 switch(type) {
1653                 case MB8877_SND_TYPE_SEEK: // SEEK
1654                         snd_seek_data = (int16_t *)malloc(alloc_size);
1655                         memcpy(snd_seek_data, data, utl_size);
1656                         strncpy(snd_seek_name, pathname, 511);
1657                         snd_seek_samples_size = dst_size;
1658                         break;
1659                 case MB8877_SND_TYPE_HEAD: // HEAD
1660                         snd_head_data = (int16_t *)malloc(alloc_size);
1661                         memcpy(snd_head_data, data, utl_size);
1662                         strncpy(snd_head_name, pathname, 511);
1663                         snd_head_samples_size = dst_size;
1664                         break;
1665                 default:
1666                         this->out_debug_log("ID=%d : Illegal type (%d): 0 (SEEK SOUND) or 1 (HEAD SOUND) is available.",
1667                                                                 id, type);
1668                         return false;
1669                 }
1670                 this->out_debug_log("ID=%d : Success to load SOUND FILE for %s:%s",
1671                                                         id, (type == 0) ? _T("SEEK") : _T("HEAD") ,
1672                                                         pathname);
1673         }
1674         return true;
1675 }
1676
1677 void MB8877::release_sound_data(int type)
1678 {
1679         switch(type) {
1680         case MB8877_SND_TYPE_SEEK: // SEEK
1681                 if(snd_seek_data != NULL) free(snd_seek_data);
1682                 memset(snd_seek_name, 0x00, sizeof(snd_seek_name));
1683                 snd_seek_data = NULL;
1684                 break;
1685         case MB8877_SND_TYPE_HEAD: // HEAD
1686                 if(snd_head_data != NULL) free(snd_head_data);
1687                 memset(snd_head_name, 0x00, sizeof(snd_head_name));
1688                 snd_head_data = NULL;
1689                         break;
1690         default:
1691                 break;
1692         }
1693 }
1694
1695 bool MB8877::reload_sound_data(int type)
1696 {
1697         switch(type) {
1698         case MB8877_SND_TYPE_SEEK: // SEEK
1699                 if(snd_seek_data != NULL) free(snd_seek_data);
1700                 break;
1701         case MB8877_SND_TYPE_HEAD:
1702                 if(snd_head_data != NULL) free(snd_head_data);
1703                 break;
1704         default:
1705                 return false;
1706                 break;
1707         }
1708         _TCHAR *p = (type == MB8877_SND_TYPE_SEEK) ? snd_seek_name : snd_head_name;
1709     _TCHAR tmps[512];
1710         strncpy(tmps, p, 511);
1711         return load_sound_data(type, tmps);
1712 }
1713
1714 void MB8877::mix_main(int32_t *dst, int count, int16_t *src, int *table, int samples)
1715 {
1716         int ptr, pp;
1717         int i, j, k;
1718         int32_t data[2];
1719         int32_t *dst_tmp;
1720         if((dst == NULL) || (src == NULL)) return;
1721         if((count <= 0) || (samples <= 0)) return;
1722         for(i=0; i < MB8877_SND_TBL_MAX; i++) {
1723                 ptr = table[i];
1724                 if(ptr >= 0) {
1725                         if(ptr < samples) {
1726                                 if(!snd_mute && (config.sound_fdd != 0)) {
1727                                         pp = ptr << 1;
1728                                         dst_tmp = dst;
1729                                         k = 0;
1730                                         for(j = 0; j < count; j++) {
1731                                                 if(ptr >= samples) {
1732                                                         break;
1733                                                 }
1734                                                 data[0] = (int32_t)src[pp + 0];
1735                                                 data[1] = (int32_t)src[pp + 1];
1736                                                 dst_tmp[k + 0] += apply_volume((int32_t)data[0], snd_level_l);
1737                                                 dst_tmp[k + 1] += apply_volume((int32_t)data[1], snd_level_r);
1738                                                 k += 2;
1739                                                 pp += 2;
1740                                                 ptr++;
1741                                         }
1742                                 } else {
1743                                         ptr += count;
1744                                 }
1745                         }
1746                         if(ptr >= samples) {
1747                                 table[i] = -1;
1748                         } else {
1749                                 table[i] = ptr;
1750                         }
1751                 }
1752         }
1753 }
1754
1755 void MB8877::mix(int32_t *buffer, int cnt)
1756 {
1757         if(snd_seek_data != NULL) mix_main(buffer, cnt, snd_seek_data, snd_seek_mix_tbl, snd_seek_samples_size);
1758         if(snd_head_data != NULL) mix_main(buffer, cnt, snd_head_data, snd_head_mix_tbl, snd_head_samples_size);
1759 }
1760
1761 void MB8877::set_volume(int ch, int decibel_l, int decibel_r)
1762 {
1763         snd_level_l = decibel_to_volume(decibel_l);
1764         snd_level_r = decibel_to_volume(decibel_r);
1765 }
1766 #endif
1767
1768 #define STATE_VERSION   7
1769
1770 void MB8877::save_state(FILEIO* state_fio)
1771 {
1772         state_fio->FputUint32(STATE_VERSION);
1773         state_fio->FputInt32(this_device_id);
1774         
1775         state_fio->Fwrite(fdc, sizeof(fdc), 1);
1776         for(int i = 0; i < MAX_DRIVE; i++) {
1777                 disk[i]->save_state(state_fio);
1778         }
1779         state_fio->FputUint8(status);
1780         state_fio->FputUint8(status_tmp);
1781         state_fio->FputUint8(cmdreg);
1782         state_fio->FputUint8(cmdreg_tmp);
1783         state_fio->FputUint8(trkreg);
1784         state_fio->FputUint8(secreg);
1785         state_fio->FputUint8(datareg);
1786         state_fio->FputUint8(drvreg);
1787         state_fio->FputUint8(sidereg);
1788         state_fio->FputUint8(cmdtype);
1789         state_fio->Fwrite(register_id, sizeof(register_id), 1);
1790         state_fio->FputBool(now_search);
1791         state_fio->FputBool(now_seek);
1792         state_fio->FputBool(sector_changed);
1793         state_fio->FputInt32(no_command);
1794         state_fio->FputInt32(seektrk);
1795         state_fio->FputBool(seekvct);
1796         state_fio->FputBool(motor_on);
1797         state_fio->FputBool(drive_sel);
1798         state_fio->FputUint32(prev_drq_clock);
1799         state_fio->FputUint32(seekend_clock);
1800 #if defined(USE_SOUND_FILES)
1801         state_fio->Fwrite(snd_seek_name, sizeof(snd_seek_name), 1);
1802         state_fio->Fwrite(snd_head_name, sizeof(snd_head_name), 1);
1803         for(int i = 0; i < MB8877_SND_TBL_MAX; i++) {
1804                 state_fio->FputInt32(snd_seek_mix_tbl[i]);
1805         }
1806         for(int i = 0; i < MB8877_SND_TBL_MAX; i++) {
1807                 state_fio->FputInt32(snd_head_mix_tbl[i]);
1808         }
1809         state_fio->FputBool(snd_mute);
1810         state_fio->FputInt32(snd_level_l);
1811         state_fio->FputInt32(snd_level_r);
1812 #endif
1813 }
1814
1815 bool MB8877::load_state(FILEIO* state_fio)
1816 {
1817         uint32_t s_version = state_fio->FgetUint32();
1818         //uint32_t desired_version = STATE_VERSION;
1819         bool pending = false;
1820         if(s_version != STATE_VERSION) {
1821                 if(s_version == 5) {
1822                         pending = true;
1823                 } else {
1824                         return false;
1825                 }
1826         }
1827         if(state_fio->FgetInt32() != this_device_id) {
1828                 return false;
1829         }
1830         state_fio->Fread(fdc, sizeof(fdc), 1);
1831         for(int i = 0; i < MAX_DRIVE; i++) {
1832                 if(!disk[i]->load_state(state_fio)) {
1833                         return false;
1834                 }
1835         }
1836         status = state_fio->FgetUint8();
1837         status_tmp = state_fio->FgetUint8();
1838         cmdreg = state_fio->FgetUint8();
1839         cmdreg_tmp = state_fio->FgetUint8();
1840         trkreg = state_fio->FgetUint8();
1841         secreg = state_fio->FgetUint8();
1842         datareg = state_fio->FgetUint8();
1843         drvreg = state_fio->FgetUint8();
1844         sidereg = state_fio->FgetUint8();
1845         cmdtype = state_fio->FgetUint8();
1846         state_fio->Fread(register_id, sizeof(register_id), 1);
1847         now_search = state_fio->FgetBool();
1848         now_seek = state_fio->FgetBool();
1849         sector_changed = state_fio->FgetBool();
1850         no_command = state_fio->FgetInt32();
1851         seektrk = state_fio->FgetInt32();
1852         seekvct = state_fio->FgetBool();
1853         motor_on = state_fio->FgetBool();
1854         drive_sel = state_fio->FgetBool();
1855         prev_drq_clock = state_fio->FgetUint32();
1856         seekend_clock = state_fio->FgetUint32();
1857 #if defined(USE_SOUND_FILES)
1858         if(!pending) {
1859                 state_fio->Fread(snd_seek_name, sizeof(snd_seek_name), 1);
1860                 state_fio->Fread(snd_head_name, sizeof(snd_head_name), 1);
1861                 for(int i = 0; i < MB8877_SND_TBL_MAX; i++) {
1862                         snd_seek_mix_tbl[i] = state_fio->FgetInt32();
1863                 }
1864                 for(int i = 0; i < MB8877_SND_TBL_MAX; i++) {
1865                         snd_head_mix_tbl[i] = state_fio->FgetInt32();
1866                 }
1867                 snd_mute = state_fio->FgetBool();
1868                 snd_level_l = state_fio->FgetInt32();
1869                 snd_level_r = state_fio->FgetInt32();
1870                 if(snd_seek_data != NULL) free(snd_seek_data);
1871                 if(snd_head_data != NULL) free(snd_head_data);
1872                 if(strlen(snd_seek_name) > 0) {
1873                         _TCHAR tmps[512];
1874                         strncpy(tmps, snd_seek_name, 511);
1875                         load_sound_data(MB8877_SND_TYPE_SEEK, (const _TCHAR *)tmps);
1876                 }
1877                 if(strlen(snd_head_name) > 0) {
1878                         _TCHAR tmps[512];
1879                         strncpy(tmps, snd_head_name, 511);
1880                         load_sound_data(MB8877_SND_TYPE_HEAD, (const _TCHAR *)tmps);
1881                 }
1882         }
1883 #endif
1884         //touch_sound();
1885         return true;
1886 }
1887