2 Skelton for retropc emulator
5 Author : Takeda.Toshiya
8 [ MB8877 / MB8876 / MB8866 / MB89311 ]
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
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
39 #define EVENT_SEEKEND_VERIFY 1
40 #define EVENT_SEARCH 2
42 #define EVENT_MULTI1 4
43 #define EVENT_MULTI2 5
45 #define EVENT_END_READ_SECTOR 7
46 #define EVENT_CRC_ERROR 8
48 //#define DRIVE_MASK (MAX_DRIVE - 1)
50 #define DELAY_TIME (disk[drvreg]->drive_type == DRIVE_TYPE_2HD ? 15000 : 30000)
52 static const int seek_wait_hi[4] = {3000, 6000, 10000, 16000}; // 2MHz
53 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000}; // 1MHz
55 void MB8877::cancel_my_event(int event)
57 if(register_id[event] != -1) {
58 cancel_event(this, register_id[event]);
59 register_id[event] = -1;
63 bool MB8877::register_my_event_with_check(int event, double usec)
65 if(register_id[event] >= 0) return false;
66 register_my_event(event, usec);
70 void MB8877::register_my_event(int event, double usec)
72 cancel_my_event(event);
73 register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, ®ister_id[event]);
76 void MB8877::register_seek_event()
78 cancel_my_event(EVENT_SEEK);
79 if(fdc[drvreg].track == seektrk) {
80 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), 300, false, ®ister_id[EVENT_SEEK]); // From XM7 v3.4L77a Wait 300uS.
81 } else if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
82 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
84 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
89 void MB8877::register_drq_event(int bytes)
91 double nusec = disk[drvreg]->get_usec_per_bytes(bytes); // For debug
92 double usec = nusec - get_passed_usec(prev_drq_clock);
95 if(usec > nusec) usec = nusec;
99 if(config.correct_disk_timing[drvreg]) {
103 if((disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER)) {
104 /* (disk[drvreg]->is_special_disk == SPECIAL_DISK_FM77AV_PSYOBLADE) || */
109 switch(disk[drvreg]->media_type) {
112 if(usec > 7.0) usec = 7.0;
124 if(fdc_debug_log) this->out_debug_log("DRQ REG: %dbytes %d:%d -> %f\n", bytes, get_current_clock(), prev_drq_clock, usec);
125 cancel_my_event(EVENT_DRQ);
126 register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, ®ister_id[EVENT_DRQ]);
129 void MB8877::register_lost_event(int bytes)
131 cancel_my_event(EVENT_LOST);
132 register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, ®ister_id[EVENT_LOST]);
135 bool MB8877::check_drive(void)
137 if(drvreg >= _max_drive) {
138 status = FDC_ST_NOTREADY;
146 bool MB8877::check_drive2(void)
151 if(!is_disk_inserted(drvreg & 0x7f)) {
152 status = FDC_ST_NOTREADY;
160 void MB8877::initialize()
162 DEVICE::initialize();
163 // initialize d88 handler
164 if(osd->check_feature(_T("MAX_DRIVE"))) {
165 _max_drive = osd->get_feature_int_value(_T("MAX_DRIVE"));
166 } else if(osd->check_feature(_T("MAX_FD"))) {
167 _max_drive = osd->get_feature_int_value(_T("MAX_FD"));
171 _drive_mask = _max_drive - 1;
172 for(int i = 0; i < _max_drive; i++) {
173 disk[i] = new DISK(emu);
174 disk[i]->set_device_name(_T("%s/Disk #%d"), this_device_name, i + 1);
177 if(osd->check_feature(_T("MB8877_NO_BUSY_AFTER_SEEK"))) {
178 mb8877_no_busy_after_seek = true;
180 //if(osd->check_feature(_T("_FDC_DEBUG_LOG"))) {
181 // fdc_debug_log = true;
183 fdc_debug_log = config.special_debug_fdc;
185 if(osd->check_feature(_T("HAS_MB8866"))) {
187 invert_registers = true;
188 set_device_name(_T("MB8866 FDC"));
190 if(osd->check_feature(_T("HAS_MB8876"))) {
191 invert_registers = true;
192 set_device_name(_T("MB8876 FDC"));
193 } else if(osd->check_feature(_T("HAS_MB89311"))) {
195 set_device_name(_T("MB89311 FDC"));
197 set_device_name(_T("MB8877 FDC"));
199 if(osd->check_feature(_T("_X1")) || osd->check_feature(_T("_X1TWIN")) ||
200 osd->check_feature(_T("_X1TURBO")) || osd->check_feature(_T("_X1TURBOZ"))) {
203 if(osd->check_feature(_T("_FM7")) || osd->check_feature(_T("_FM8")) ||
204 osd->check_feature(_T("_FM77_VARIANTS")) || osd->check_feature(_T("_FM77AV_VARIANTS"))) {
206 if(osd->check_feature(_T("_FM77AV40")) || osd->check_feature(_T("_FM77AV40EX")) ||
207 osd->check_feature(_T("_FM77AV40SX")) ||
208 osd->check_feature(_T("_FM77AV20")) || osd->check_feature(_T("_FM77AV20EX"))) {
209 type_fm77av_2dd = true;
212 if(osd->check_feature(_T("_FMR50"))) {
215 if(osd->check_feature(_T("_FMR60"))) {
220 if(d_noise_seek != NULL) {
221 d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)"));
222 if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) {
223 if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) {
224 d_noise_seek->load_wav_file(_T("SEEK.WAV"));
227 d_noise_seek->set_mute(!config.sound_noise_fdd);
229 if(d_noise_head_down != NULL) {
230 d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)"));
231 d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV"));
232 d_noise_head_down->set_mute(!config.sound_noise_fdd);
234 if(d_noise_head_up != NULL) {
235 d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)"));
236 d_noise_head_up->load_wav_file(_T("HEADUP.WAV"));
237 d_noise_head_up->set_mute(!config.sound_noise_fdd);
241 memset(fdc, 0, sizeof(fdc));
242 for(int i = 0; i < 16; i++) {
243 fdc[i].track = 40; // OK?
244 fdc[i].count_immediate = false;
249 status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
251 prev_drq_clock = seekend_clock = 0;
254 void MB8877::release()
256 // release d88 handler
257 for(int i = 0; i < _max_drive; i++) {
267 for(int i = 0; i < _max_drive; i++) {
268 //fdc[i].track = 0; // Hold to track
270 fdc[i].access = false;
272 for(int i = 0; i < (int)array_length(register_id); i++) {
275 now_search = now_seek = drive_sel = false;
280 extended_mode = true;
282 extended_mode = false;
287 void MB8877::write_io8(uint32_t addr, uint32_t data)
295 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
296 if(invert_registers) {
297 cmdreg = (~data) & 0xff;
308 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
309 if(invert_registers) {
310 trkreg = (~data) & 0xff;
316 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
317 // track reg is written after command starts
318 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
325 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
326 if(invert_registers) {
327 secreg = (~data) & 0xff;
333 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
334 // sector reg is written after command starts
335 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
342 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
343 if(invert_registers) {
344 datareg = (~data) & 0xff;
350 ready = ((status & FDC_ST_DRQ) && !now_search);
351 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
353 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
354 if(!motor_on) ready = false;
359 if(!motor_on) ready = false;
361 // if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
363 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
364 // write or multisector write
365 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
366 if(!disk[drvreg]->write_protected) {
367 if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
368 disk[drvreg]->sector[fdc[drvreg].index] = datareg;
369 sector_changed = true;
372 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
374 status |= FDC_ST_WRITEFAULT;
375 status &= ~FDC_ST_BUSY;
379 //fdc[drvreg].index++;
381 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
382 if(cmdtype == FDC_CMD_WR_SEC) {
384 //#ifdef _FDC_DEBUG_LOG
385 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
387 status &= ~FDC_ST_BUSY;
392 //#ifdef _FDC_DEBUG_LOG
393 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
395 // 2HD: 360rpm, 10410bytes/track -> 0.06246bytes/us
396 register_my_event(EVENT_MULTI1, 30); // 0.06246bytes/us * 30us = 1.8738bytes < GAP3
397 register_my_event(EVENT_MULTI2, 60); // 0.06246bytes/us * 60us = 3.7476bytes < GAP3
399 sector_changed = false;
400 } else if(status & FDC_ST_DRQ) {
401 if(fdc[drvreg].index == 0) {
402 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
404 register_drq_event(1);
406 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
408 status &= ~FDC_ST_DRQ;
409 } else if(cmdtype == FDC_CMD_WR_TRK) {
411 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
412 if(!disk[drvreg]->write_protected) {
413 if(fdc[drvreg].index == 0) {
414 disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
415 fdc[drvreg].id_written = false;
416 fdc[drvreg].side = sidereg;
417 fdc[drvreg].side_changed = false;
419 if(fdc[drvreg].side != sidereg) {
420 fdc[drvreg].side_changed = true;
422 if(fdc[drvreg].side_changed) {
423 // abort write track because disk side is changed
424 } else if(datareg == 0xf5) {
425 // write a1h in missing clock
426 } else if(datareg == 0xf6) {
427 // write c2h in missing clock
428 } else if(datareg == 0xf7) {
430 if(!fdc[drvreg].id_written) {
431 // insert new sector with data crc error
433 uint8_t c = 0, h = 0, r = 0, n = 0;
434 fdc[drvreg].id_written = true;
435 fdc[drvreg].sector_found = false;
436 if(fdc[drvreg].index >= 4) {
437 c = disk[drvreg]->track[fdc[drvreg].index - 4];
438 h = disk[drvreg]->track[fdc[drvreg].index - 3];
439 r = disk[drvreg]->track[fdc[drvreg].index - 2];
440 n = disk[drvreg]->track[fdc[drvreg].index - 1];
442 fdc[drvreg].sector_length = 0x80 << (n & 3);
443 fdc[drvreg].sector_index = 0;
444 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
445 } else if(fdc[drvreg].sector_found) {
446 // clear data crc error if all sector data are written
447 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
448 disk[drvreg]->set_data_crc_error(false);
450 fdc[drvreg].id_written = false;
452 // data mark of current sector is not written
453 disk[drvreg]->set_data_mark_missing();
456 } else if(fdc[drvreg].id_written) {
457 if(fdc[drvreg].sector_found) {
459 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
460 disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
462 fdc[drvreg].sector_index++;
463 } else if(datareg == 0xf8 || datareg == 0xfb) {
465 disk[drvreg]->set_deleted(datareg == 0xf8);
466 fdc[drvreg].sector_found = true;
469 disk[drvreg]->track[fdc[drvreg].index] = datareg;
471 status |= FDC_ST_WRITEFAULT;
472 status &= ~FDC_ST_BUSY;
473 status &= ~FDC_ST_DRQ;
477 //fdc[drvreg].index++;
479 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
480 if(!disk[drvreg]->write_protected) {
481 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
482 // data mark of last sector is not written
483 disk[drvreg]->set_data_mark_missing();
485 disk[drvreg]->sync_buffer();
487 status &= ~FDC_ST_BUSY;
490 } else if(status & FDC_ST_DRQ) {
491 if(fdc[drvreg].index == 0) {
492 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
494 register_drq_event(1);
496 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
498 status &= ~FDC_ST_DRQ;
500 if(!(status & FDC_ST_DRQ)) {
501 cancel_my_event(EVENT_LOST);
503 fdc[drvreg].access = true;
510 uint32_t MB8877::read_io8(uint32_t addr)
513 bool not_ready = false;
523 // disk not inserted, motor stop
524 not_ready = !disk[drvreg]->inserted;
525 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
527 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
528 if(!motor_on) not_ready = true;
533 if(!motor_on) not_ready = true;
535 // if(!disk[drvreg]->inserted || !motor_on) {
537 status |= FDC_ST_NOTREADY;
539 status &= ~FDC_ST_NOTREADY;
542 if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
543 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
544 status |= FDC_ST_WRITEP;
546 status &= ~FDC_ST_WRITEP;
549 status &= ~FDC_ST_WRITEP;
551 // track0, index hole
552 if(cmdtype == FDC_CMD_TYPE1) {
553 if(fdc[drvreg].track == 0) {
554 status |= FDC_ST_TRACK00;
556 status &= ~FDC_ST_TRACK00;
558 // index hole signal width is 5msec (thanks Mr.Sato)
559 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
560 status |= FDC_ST_INDEX;
562 status &= ~FDC_ST_INDEX;
565 // show busy a moment
567 if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
568 status &= ~FDC_ST_BUSY;
569 //#ifdef MB8877_NO_BUSY_AFTER_SEEK
570 if(mb8877_no_busy_after_seek) {
571 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
573 if(disk[0]->is_special_disk != SPECIAL_DISK_FM7_XANADU2_D) {
586 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
587 // MZ-2000 HuBASIC invites NOT READY status
588 if(++no_command == 16) {
589 val |= FDC_ST_NOTREADY;
596 if(!(status & FDC_ST_DRQ)) {
599 if(!(status & FDC_ST_BUSY)) {
602 //#ifdef _FDC_DEBUG_LOG
603 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
605 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
606 if(invert_registers) {
607 return (~val) & 0xff;
615 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
616 if(invert_registers) {
617 return (~trkreg) & 0xff;
625 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
626 if(invert_registers) {
627 return (~secreg) & 0xff;
635 ready = ((status & FDC_ST_DRQ) && !now_search);
636 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
638 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
639 if(!motor_on) ready = false;
644 if(!motor_on) ready = false;
646 // if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
648 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
649 // read or multisector read
650 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
651 datareg = disk[drvreg]->sector[fdc[drvreg].index];
653 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) { // Reading complete this sector.
655 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
658 //#ifdef _FDC_DEBUG_LOG
659 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
661 status |= FDC_ST_CRCERR;
662 status &= ~FDC_ST_BUSY;
666 register_my_event_with_check(EVENT_CRC_ERROR, disk[drvreg]->get_usec_per_bytes(3));
668 } else if(cmdtype == FDC_CMD_RD_SEC) {
671 //#ifdef _FDC_DEBUG_LOG
672 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
674 status &= ~FDC_ST_BUSY;
678 register_my_event_with_check(EVENT_END_READ_SECTOR, disk[drvreg]->get_usec_per_bytes(3));
682 //#ifdef _FDC_DEBUG_LOG
683 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
685 register_my_event(EVENT_MULTI1, 30);
686 register_my_event(EVENT_MULTI2, 60);
688 } else { // Still data remain.
689 register_drq_event(1);
690 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
692 status &= ~FDC_ST_DRQ;
693 } else if(cmdtype == FDC_CMD_RD_ADDR) {
695 if(fdc[drvreg].index < 6) {
696 datareg = disk[drvreg]->id[fdc[drvreg].index];
697 //fdc[drvreg].index++;
699 if((fdc[drvreg].index + 1) >= 6) {
700 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
702 status |= FDC_ST_CRCERR;
704 status &= ~FDC_ST_BUSY;
707 //#ifdef _FDC_DEBUG_LOG
708 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
711 register_drq_event(1);
712 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
714 status &= ~FDC_ST_DRQ;
715 } else if(cmdtype == FDC_CMD_RD_TRK) {
717 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
718 datareg = disk[drvreg]->track[fdc[drvreg].index];
719 //fdc[drvreg].index++;
721 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
722 //#ifdef _FDC_DEBUG_LOG
723 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF TRACK\n"));
725 status &= ~FDC_ST_BUSY;
726 status |= FDC_ST_LOSTDATA;
730 register_drq_event(1);
731 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
733 status &= ~FDC_ST_DRQ;
735 if(!(status & FDC_ST_DRQ)) {
736 cancel_my_event(EVENT_LOST);
738 fdc[drvreg].access = true;
741 //#ifdef _FDC_DEBUG_LOG
742 if(fdc_debug_log) this->force_out_debug_log(_T("FDC\tDATA=%2x\n"), datareg); // emu->force_out_debug_log()
744 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
745 if(invert_registers) {
746 return (~datareg) & 0xff;
756 void MB8877::write_dma_io8(uint32_t addr, uint32_t data)
761 uint32_t MB8877::read_dma_io8(uint32_t addr)
766 void MB8877::write_signal(int id, uint32_t data, uint32_t mask)
768 if(id == SIG_MB8877_DRIVEREG) {
769 drvreg = data & _drive_mask;
771 seekend_clock = get_current_clock();
772 } else if(id == SIG_MB8877_SIDEREG) {
773 sidereg = (data & mask) ? 1 : 0;
774 } else if(id == SIG_MB8877_MOTOR) {
775 motor_on = ((data & mask) != 0);
779 uint32_t MB8877::read_signal(int ch)
781 if(ch == SIG_MB8877_DRIVEREG) {
782 return drvreg & _drive_mask;
783 } else if(ch == SIG_MB8877_SIDEREG) {
785 } else if(ch == SIG_MB8877_MOTOR) {
786 return motor_on ? 1 : 0;
791 for(int i = 0; i < _max_drive; i++) {
795 fdc[i].access = false;
803 void MB8877::event_callback(int event_id, int err)
805 int event = event_id >> 8;
806 int cmd = event_id & 0xff;
807 //bool need_after_irq = false;
808 register_id[event] = -1;
810 // cancel event if the command is finished or other command is executed
812 if(event == EVENT_SEEK || event == EVENT_SEEKEND_VERIFY) {
814 } else if(event == EVENT_SEARCH) {
822 //#ifdef _FDC_DEBUG_LOG
823 //this->out_debug_log(_T("FDC\tSEEK START\n"));
825 if(seektrk > fdc[drvreg].track) {
827 if(d_noise_seek != NULL) d_noise_seek->play();
828 //need_after_irq = true;
829 } else if(seektrk < fdc[drvreg].track) {
831 if(d_noise_seek != NULL) d_noise_seek->play();
832 //need_after_irq = true;
834 // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
835 if((((cmdreg & 0x10) != 0) && (cmdreg >= 0x20)) || ((cmdreg & 0xf0) == 0)) {
836 trkreg = fdc[drvreg].track;
838 if((uint8_t)seektrk != (uint8_t)fdc[drvreg].track) {
839 //if(need_after_irq) {
841 //set_irq(true); // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
843 register_seek_event();
847 // Verify before execute command.
848 // Port from XM7.Thanks to Ryu Takegami.
851 if(trkreg != fdc[drvreg].track) {
852 status |= FDC_ST_SEEKERR;
853 trkreg = fdc[drvreg].track; // Reload track register when verify: Really OK? 20180224 K.O
857 seekend_clock = get_current_clock();
861 if((cmdreg & 0xf4) == 0x44) {
865 } else if((cmdreg & 0xf4) == 0x64) {
876 status_tmp |= search_track();
879 if(status_tmp & FDC_ST_SEEKERR) {
880 time = get_usec_to_detect_index_hole(5, true);
882 time = get_usec_to_next_trans_pos(true);
885 // Verify wait is shorter than above at least FM-7 series's MB8877. 20180223 K.O.
886 // Thanks to Ryu Takegami.
887 time = (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ? 10000.0 : 20000.0;
889 // 20180128 K.O If seek completed, delay to make IRQ/DRQ at SEEKEND_VERIFY.
890 register_my_event(EVENT_SEEKEND_VERIFY, time);
893 // case EVENT_SEEKEND: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
896 status &= (uint8_t)(~FDC_ST_BUSY); // 20180118 K.O Turn off BUSY flag.
898 set_irq(true); // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
900 case EVENT_SEEKEND_VERIFY: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
902 status &= (uint8_t)(~FDC_ST_BUSY); // 20180118 K.O Turn off BUSY flag.
904 set_irq(true); // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
905 //#ifdef _FDC_DEBUG_LOG
906 //this->out_debug_log(_T("FDC\tSEEK END\n"));
911 if(status_tmp & FDC_ST_RECNFND) {
912 //#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
914 // for SHARP X1 Batten Tanuki
915 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
916 status_tmp &= ~FDC_ST_RECNFND;
920 //#ifdef _FDC_DEBUG_LOG
921 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH NG\n"));
923 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
927 } else if(status_tmp & FDC_ST_WRITEFAULT) {
928 //#ifdef _FDC_DEBUG_LOG
929 if(fdc_debug_log) this->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
931 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
936 status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
937 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
938 register_lost_event(8);
939 } else if(cmdtype == FDC_CMD_WR_TRK) {
940 register_lost_event(3);
942 register_lost_event(1);
944 fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
945 fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
948 if(fdc_debug_log) this->out_debug_log("DRQ ON@SEARCH: %d\n", prev_drq_clock);
949 //#ifdef _FDC_DEBUG_LOG
950 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH OK\n"));
955 if(status & FDC_ST_BUSY) {
956 status |= FDC_ST_DRQ;
957 register_lost_event(1);
958 if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
959 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
961 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
963 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
964 cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
965 cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK ||
966 cmdtype == FDC_CMD_RD_ADDR) {
967 if(!(fdc[drvreg].count_immediate)) fdc[drvreg].index++;
969 fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
972 if(fdc_debug_log) this->out_debug_log("DRQ ON@DRQ: %d\n", prev_drq_clock);
973 //#ifdef _FDC_DEBUG_LOG
974 //this->out_debug_log(_T("FDC\tDRQ!\n"));
982 if(cmdtype == FDC_CMD_RD_MSEC) {
984 } else if(cmdtype == FDC_CMD_WR_MSEC) {
985 cmd_writedata(false);
988 case EVENT_CRC_ERROR:
989 //#ifdef _FDC_DEBUG_LOG
990 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
992 status |= FDC_ST_CRCERR;
993 status &= ~FDC_ST_BUSY;
997 case EVENT_END_READ_SECTOR:
998 //#ifdef _FDC_DEBUG_LOG
999 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
1001 status &= ~FDC_ST_BUSY;
1006 if(status & FDC_ST_BUSY) {
1007 //#ifdef _FDC_DEBUG_LOG
1008 if(fdc_debug_log) this->out_debug_log(_T("FDC\tDATA LOST\n"));
1010 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
1011 if(fdc[drvreg].index == 0) { // HEAD of REGION
1013 //if((status & FDC_ST_DRQ) != 0) { // 20180130 FORCE DOWN DRQ ON LOST-DATA.
1014 status |= FDC_ST_LOSTDATA;
1015 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1019 } else { // STILL WRITING
1021 //if((status & FDC_ST_DRQ) != 0) {
1022 status |= FDC_ST_LOSTDATA;
1023 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1030 //if((status & FDC_ST_DRQ) != 0) {
1031 status |= FDC_ST_LOSTDATA;
1032 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1038 status |= FDC_ST_LOSTDATA;
1044 // ----------------------------------------------------------------------------
1046 // ----------------------------------------------------------------------------
1048 void MB8877::process_cmd()
1053 //#ifdef HAS_MB89311
1054 // MB89311 mode commands
1056 if(cmdreg == 0xfc) {
1057 // delay (may be only in extended mode)
1058 //#ifdef _FDC_DEBUG_LOG
1059 if(fdc_debug_log) 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);
1061 cmdtype = status = 0;
1063 } else if(cmdreg == 0xfd) {
1065 //#ifdef _FDC_DEBUG_LOG
1066 if(fdc_debug_log) 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);
1068 cmdtype = status = 0;
1070 } else if(cmdreg == 0xfe) {
1072 //#ifdef _FDC_DEBUG_LOG
1073 if(fdc_debug_log) 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);
1075 extended_mode = !extended_mode;
1076 cmdtype = status = 0;
1078 } else if(cmdreg == 0xff) {
1079 // reset (may be only in extended mode)
1080 //#ifdef _FDC_DEBUG_LOG
1081 if(fdc_debug_log) 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);
1084 extended_mode = true;
1086 } else if(extended_mode) {
1088 if((cmdreg & 0xeb) == 0x21) {
1089 //#ifdef _FDC_DEBUG_LOG
1090 if(fdc_debug_log) 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);
1094 } else if((cmdreg & 0xeb) == 0x22) {
1095 //#ifdef _FDC_DEBUG_LOG
1096 if(fdc_debug_log) 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);
1101 } else if((cmdreg & 0xf4) == 0x44) {
1103 //#ifdef _FDC_DEBUG_LOG
1104 if(fdc_debug_log) 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);
1108 } else if((cmdreg & 0xf4) == 0x64) {
1110 //#ifdef _FDC_DEBUG_LOG
1111 if(fdc_debug_log) 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);
1116 } else if((cmdreg & 0xfb) == 0xf1) {
1118 //#ifdef _FDC_DEBUG_LOG
1119 if(fdc_debug_log) 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);
1128 // MB8877 mode commands
1129 //#ifdef _FDC_DEBUG_LOG
1130 static const _TCHAR *cmdstr[0x10] = {
1131 _T("RESTORE "), _T("SEEK "), _T("STEP "), _T("STEP "),
1132 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
1133 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
1134 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
1136 if(fdc_debug_log) 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);
1138 switch(cmdreg & 0xf8) {
1140 case 0x00: case 0x08:
1142 update_head_flag(drvreg, (cmdreg & 8) != 0);
1144 case 0x10: case 0x18:
1146 update_head_flag(drvreg, (cmdreg & 8) != 0);
1148 case 0x20: case 0x28:
1149 case 0x30: case 0x38:
1151 update_head_flag(drvreg, (cmdreg & 8) != 0);
1153 case 0x40: case 0x48:
1154 case 0x50: case 0x58:
1156 update_head_flag(drvreg, (cmdreg & 8) != 0);
1158 case 0x60: case 0x68:
1159 case 0x70: case 0x78:
1161 update_head_flag(drvreg, (cmdreg & 8) != 0);
1164 case 0x80: case 0x88:
1165 case 0x90: case 0x98:
1167 update_head_flag(drvreg, true);
1169 case 0xa0:case 0xa8:
1170 case 0xb0: case 0xb8:
1171 cmd_writedata(true);
1172 update_head_flag(drvreg, true);
1177 update_head_flag(drvreg, true);
1181 update_head_flag(drvreg, true);
1185 update_head_flag(drvreg, true);
1188 case 0xd0: case 0xd8:
1197 void MB8877::cmd_restore()
1200 cmdtype = FDC_CMD_TYPE1;
1201 if(!check_drive()) {
1204 if((cmdreg & 0x08) != 0) { // Head engage
1205 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1207 status = FDC_ST_BUSY;
1212 if(fdc[drvreg].track < 0) {
1213 fdc[drvreg].track = 0;
1215 } else if(fdc[drvreg].track >= 80) {
1216 fdc[drvreg].track = 80;
1219 trkreg = fdc[drvreg].track;
1226 register_seek_event();
1229 void MB8877::cmd_seek()
1232 cmdtype = FDC_CMD_TYPE1;
1233 if(!check_drive()) {
1236 if((cmdreg & 0x08) != 0) { // Head engage
1237 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1239 status = FDC_ST_BUSY;
1242 // revert below 20180225 K.O
1243 //seektrk = (uint8_t)(fdc[drvreg].track + datareg - trkreg); // Seek target is differ when drive's track != trkreg.Thanks to Haserin and Ryu Takegami.
1244 seektrk = (int)((int8_t)datareg);
1245 //#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
1246 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1247 seektrk = disk[drvreg]->get_max_tracks() - 1;
1248 } else if(seektrk < 0) {
1252 seekvct = !(seektrk > fdc[drvreg].track);
1253 // Update track register by data register.Thanks to Ryu Takegami. 20180224 K.O
1257 register_seek_event();
1260 void MB8877::cmd_step()
1270 void MB8877::cmd_stepin()
1273 cmdtype = FDC_CMD_TYPE1;
1274 if(!check_drive()) {
1277 if((cmdreg & 0x08) != 0) { // Head engage
1278 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1280 status = FDC_ST_BUSY;
1283 // Verify before execute command.
1284 // Port from XM7.Thanks to Ryu Takegami.
1288 if(trkreg != fdc[drvreg].track) {
1289 status |= FDC_ST_SEEKERR;
1290 // trkreg = fdc[drvreg].track;
1294 seektrk = fdc[drvreg].track + 1;
1295 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1296 seektrk = disk[drvreg]->get_max_tracks() - 1;
1297 } else if(seektrk < 0) {
1303 register_seek_event();
1306 void MB8877::cmd_stepout()
1309 cmdtype = FDC_CMD_TYPE1;
1310 if(!check_drive()) {
1313 if((cmdreg & 0x08) != 0) { // Head engage
1314 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1316 status = FDC_ST_BUSY;
1319 // Verify before execute command.
1320 // Port from XM7.Thanks to Ryu Takegami.
1324 if(trkreg != fdc[drvreg].track) {
1325 status |= FDC_ST_SEEKERR;
1326 // trkreg = fdc[drvreg].track;
1330 seektrk = fdc[drvreg].track - 1;
1331 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1332 seektrk = disk[drvreg]->get_max_tracks() - 1;
1333 } else if(seektrk < 0) {
1337 register_seek_event();
1340 void MB8877::cmd_readdata(bool first_sector)
1343 cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
1344 if(!check_drive2()) {
1347 status = FDC_ST_BUSY;
1348 status_tmp = search_sector();
1352 if(status_tmp & FDC_ST_RECNFND) {
1353 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1355 time = get_usec_to_start_trans(first_sector);
1357 register_my_event(EVENT_SEARCH, time);
1358 cancel_my_event(EVENT_LOST);
1361 void MB8877::cmd_writedata(bool first_sector)
1363 // type-2 write data
1364 cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
1365 if(!check_drive2()) {
1368 status = FDC_ST_BUSY;
1369 status_tmp = search_sector() & ~FDC_ST_RECTYPE;
1371 sector_changed = false;
1374 if(status_tmp & FDC_ST_RECNFND) {
1375 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1376 } else if(status & FDC_ST_WRITEFAULT) {
1377 time = (cmdreg & 4) ? DELAY_TIME : 1;
1379 time = get_usec_to_start_trans(first_sector);
1381 register_my_event(EVENT_SEARCH, time);
1382 cancel_my_event(EVENT_LOST);
1385 void MB8877::cmd_readaddr()
1387 // type-3 read address
1388 cmdtype = FDC_CMD_RD_ADDR;
1389 if(!check_drive2()) {
1392 status = FDC_ST_BUSY;
1393 status_tmp = search_addr();
1397 if(status_tmp & FDC_ST_RECNFND) {
1398 time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
1400 time = get_usec_to_start_trans(true);
1402 register_my_event(EVENT_SEARCH, time);
1403 cancel_my_event(EVENT_LOST);
1406 void MB8877::cmd_readtrack()
1408 // type-3 read track
1409 cmdtype = FDC_CMD_RD_TRK;
1410 if(!check_drive2()) {
1413 status = FDC_ST_BUSY;
1416 disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1417 fdc[drvreg].index = 0;
1420 fdc[drvreg].next_trans_position = 1;
1421 double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
1422 register_my_event(EVENT_SEARCH, time);
1423 cancel_my_event(EVENT_LOST);
1426 void MB8877::cmd_writetrack()
1428 // type-3 write track
1429 cmdtype = FDC_CMD_WR_TRK;
1430 if(!check_drive2()) {
1433 status = FDC_ST_BUSY;
1436 fdc[drvreg].index = 0;
1437 fdc[drvreg].id_written = false;
1441 if(disk[drvreg]->write_protected) {
1442 status_tmp = FDC_ST_WRITEFAULT;
1443 time = (cmdreg & 4) ? DELAY_TIME : 1;
1446 // wait 15msec before raise first drq
1447 fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1450 // raise first drq soon
1451 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1452 time = disk[drvreg]->get_usec_per_bytes(1);
1454 // wait at least 3bytes before check index hole and raise second drq
1455 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1456 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1457 fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1460 register_my_event(EVENT_SEARCH, time);
1461 cancel_my_event(EVENT_LOST);
1464 //#ifdef HAS_MB89311
1465 void MB8877::cmd_format()
1468 // type-3 format (FIXME: need to implement)
1469 cmdtype = FDC_CMD_WR_TRK;
1470 status = FDC_ST_BUSY;
1473 fdc[drvreg].index = 0;
1474 fdc[drvreg].id_written = false;
1477 status_tmp = FDC_ST_WRITEFAULT;
1478 double time = (cmdreg & 4) ? DELAY_TIME : 1;
1480 register_my_event(EVENT_SEARCH, time);
1481 cancel_my_event(EVENT_LOST);
1486 void MB8877::cmd_forceint()
1488 // type-4 force interrupt
1489 bool ready = (cmdtype == 0);
1490 bool is_busy = ((status & FDC_ST_BUSY) != 0);
1491 bool now_seek_bak = now_seek;
1492 bool is_type1 = (cmdtype == FDC_CMD_TYPE1);
1493 bool is_read = ((cmdtype == FDC_CMD_RD_SEC) || (cmdtype == FDC_CMD_RD_MSEC) || \
1494 (cmdtype == FDC_CMD_RD_ADDR) || (cmdtype == FDC_CMD_RD_TRK));
1495 bool is_trkwrite = (cmdtype == FDC_CMD_WR_TRK);
1497 if(cmdtype == FDC_CMD_TYPE1) {
1498 // abort restore/seek/step command
1500 if(seektrk > fdc[drvreg].track) {
1501 if((fdc[drvreg].track < 0xff) && (fdc[drvreg].track >= 0)) fdc[drvreg].track++;
1502 } else if(seektrk < fdc[drvreg].track) {
1503 if((fdc[drvreg].track <= 0xff) && (fdc[drvreg].track > 0)) fdc[drvreg].track--;
1505 // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
1506 if((((cmdreg_tmp & 0x10) != 0) && (cmdreg_tmp >= 0x20)) || ((cmdreg_tmp & 0xf0) == 0)) {
1507 trkreg = fdc[drvreg].track;
1510 } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1511 // abort write sector command
1512 if(sector_changed) {
1513 disk[drvreg]->set_data_crc_error(false);
1515 } else if(cmdtype == FDC_CMD_WR_TRK) {
1516 // abort write track command
1517 if(!disk[drvreg]->write_protected) {
1518 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1519 // data mark of last sector is not written
1520 disk[drvreg]->set_data_mark_missing();
1522 disk[drvreg]->sync_buffer();
1525 now_search = now_seek = sector_changed = false;
1527 //#ifdef HAS_MB89311
1528 if((cmdreg == 0xff) && (type_mb89311)) {
1530 cmdtype = FDC_CMD_TYPE1;
1531 status = FDC_ST_HEADENG;
1534 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1535 cmdtype = FDC_CMD_TYPE1;
1536 if(!type_fm7) status = FDC_ST_HEADENG; // Hack for FUKUALL.d77 .
1538 status &= ~FDC_ST_BUSY;
1539 if((now_seek_bak) || (ready)) {// Refer from XM7, is this write implement? 20180210 K.O
1541 if(((ready) || !(is_busy)) && (!type_fm7)) status = FDC_ST_HEADENG;
1543 if(check_drive2()) {
1544 if(!(is_read) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEP;
1545 if((is_trkwrite) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEFAULT;
1547 if(fdc[drvreg].track == 0) status |= FDC_ST_TRACK00;
1548 if(get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
1549 status |= FDC_ST_INDEX;
1554 // force interrupt if bit0-bit3 is high
1558 //#ifdef HAS_MB89311
1562 cancel_my_event(EVENT_SEEK);
1563 cancel_my_event(EVENT_SEEKEND_VERIFY);
1564 cancel_my_event(EVENT_SEARCH);
1565 cancel_my_event(EVENT_DRQ);
1566 cancel_my_event(EVENT_MULTI1);
1567 cancel_my_event(EVENT_MULTI2);
1568 cancel_my_event(EVENT_LOST);
1571 void MB8877::update_head_flag(int drv, bool head_load)
1573 if(fdc[drv].head_load != head_load) {
1575 if(d_noise_head_down != NULL) d_noise_head_down->play();
1577 if(d_noise_head_up != NULL) d_noise_head_up->play();
1579 fdc[drv].head_load = head_load;
1583 // ----------------------------------------------------------------------------
1585 // ----------------------------------------------------------------------------
1587 uint8_t MB8877::search_track()
1590 int track = fdc[drvreg].track;
1592 if(!disk[drvreg]->get_track(track, sidereg)){
1593 return FDC_ST_SEEKERR;
1596 // verify track number
1597 if(disk[drvreg]->ignore_crc()) {
1598 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1599 disk[drvreg]->get_sector(-1, -1, i);
1600 if(disk[drvreg]->id[0] == trkreg) {
1601 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1602 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1607 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1608 disk[drvreg]->get_sector(-1, -1, i);
1609 if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1610 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1611 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1615 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1616 disk[drvreg]->get_sector(-1, -1, i);
1617 if(disk[drvreg]->id[0] == trkreg) {
1618 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1622 return FDC_ST_SEEKERR;
1625 uint8_t MB8877::search_sector()
1628 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1629 if(disk[drvreg]->write_protected) {
1630 return FDC_ST_WRITEFAULT;
1635 int track = fdc[drvreg].track;
1638 if(!disk[drvreg]->get_track(track, sidereg)) {
1639 return FDC_ST_RECNFND;
1642 // get current position
1643 int sector_num = disk[drvreg]->sector_num.sd;
1644 int position = get_cur_position();
1646 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1647 position -= disk[drvreg]->get_track_size();
1650 // first scanned sector
1651 int first_sector = 0;
1652 for(int i = 0; i < sector_num; i++) {
1653 if(position < disk[drvreg]->am1_position[i]) {
1660 for(int i = 0; i < sector_num; i++) {
1662 int index = (first_sector + i) % sector_num;
1663 disk[drvreg]->get_sector(-1, -1, index);
1666 if(disk[drvreg]->id[0] != trkreg) {
1669 //#if !defined(HAS_MB8866)
1671 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1676 if(disk[drvreg]->id[2] != secreg) {
1679 if(disk[drvreg]->sector_size.sd == 0) {
1682 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1684 disk[drvreg]->sector_size.sd = 0;
1685 return FDC_ST_RECNFND | FDC_ST_CRCERR;
1689 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1690 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
1691 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
1693 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
1695 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[index];
1696 fdc[drvreg].index = 0;
1697 //#ifdef _FDC_DEBUG_LOG
1698 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1699 disk[drvreg]->sector_size.sd,
1700 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1701 disk[drvreg]->id[4], disk[drvreg]->id[5],
1702 disk[drvreg]->data_crc_error ? 1 : 0);
1704 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1708 disk[drvreg]->sector_size.sd = 0;
1709 return FDC_ST_RECNFND;
1712 uint8_t MB8877::search_addr()
1715 int track = fdc[drvreg].track;
1717 if(!disk[drvreg]->get_track(track, sidereg)) {
1718 return FDC_ST_RECNFND;
1721 // get current position
1722 int sector_num = disk[drvreg]->sector_num.sd;
1723 int position = get_cur_position();
1725 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1726 position -= disk[drvreg]->get_track_size();
1729 // first scanned sector
1730 int first_sector = 0;
1731 for(int i = 0; i < sector_num; i++) {
1732 if(position < disk[drvreg]->am1_position[i]) {
1739 if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1740 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
1741 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
1742 fdc[drvreg].index = 0;
1743 secreg = disk[drvreg]->id[0];
1748 disk[drvreg]->sector_size.sd = 0;
1749 return FDC_ST_RECNFND;
1752 // ----------------------------------------------------------------------------
1754 // ----------------------------------------------------------------------------
1756 int MB8877::get_cur_position()
1758 return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1761 double MB8877::get_usec_to_start_trans(bool first_sector)
1763 // get time from current position
1764 double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
1765 if(first_sector && time < 60000 - get_passed_usec(seekend_clock)) {
1766 time += disk[drvreg]->get_usec_per_track();
1771 double MB8877::get_usec_to_next_trans_pos(bool delay)
1773 int position = get_cur_position();
1775 if(disk[drvreg]->invalid_format) {
1776 // XXX: this track is invalid format and the calculated sector position may be incorrect.
1777 // so use the constant period
1779 } else if(/*disk[drvreg]->no_skew && */!disk[drvreg]->correct_timing()) {
1780 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1781 // so use the period to search the next sector from the current position
1782 int sector_num = disk[drvreg]->sector_num.sd;
1785 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1786 position -= disk[drvreg]->get_track_size();
1788 for(int i = 0; i < sector_num; i++) {
1789 if(position < disk[drvreg]->am1_position[i]) {
1790 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1791 bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
1793 bytes = (disk[drvreg]->data_position[i] + 1) - position;
1796 bytes += disk[drvreg]->get_track_size(); // to make sure
1802 return disk[drvreg]->get_usec_per_bytes(bytes);
1807 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1809 int bytes = fdc[drvreg].next_trans_position - position;
1810 if(fdc[drvreg].next_am1_position < position || bytes < 0) {
1811 bytes += disk[drvreg]->get_track_size();
1813 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1820 double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
1822 int position = get_cur_position();
1824 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1826 int bytes = disk[drvreg]->get_track_size() * count - position;
1828 bytes += disk[drvreg]->get_track_size();
1830 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1837 // ----------------------------------------------------------------------------
1839 // ----------------------------------------------------------------------------
1841 void MB8877::set_irq(bool val)
1843 write_signals(&outputs_irq, val ? 0xffffffff : 0);
1846 void MB8877::set_drq(bool val)
1848 write_signals(&outputs_drq, val ? 0xffffffff : 0);
1851 // ----------------------------------------------------------------------------
1853 // ----------------------------------------------------------------------------
1855 void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
1857 if(drv < _max_drive) {
1858 disk[drv]->open(file_path, bank);
1859 #if defined(_USE_QT)
1860 if((disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) || (config.disk_count_immediate[drv])) {
1862 if(disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) {
1864 fdc[drv].count_immediate = true;
1866 fdc[drv].count_immediate = false;
1871 void MB8877::close_disk(int drv)
1873 if(drv < _max_drive) {
1876 update_head_flag(drvreg, false);
1877 fdc[drv].count_immediate = false;
1881 bool MB8877::is_disk_inserted(int drv)
1883 if(drv < _max_drive) {
1884 return disk[drv]->inserted;
1889 void MB8877::is_disk_protected(int drv, bool value)
1891 if(drv < _max_drive) {
1892 disk[drv]->write_protected = value;
1896 bool MB8877::is_disk_protected(int drv)
1898 if(drv < _max_drive) {
1899 return disk[drv]->write_protected;
1904 uint8_t MB8877::media_type(int drv)
1906 if(drv < _max_drive) {
1907 if(disk[drv]->inserted) {
1908 return disk[drv]->media_type;
1911 return MEDIA_TYPE_UNK;
1914 void MB8877::set_drive_type(int drv, uint8_t type)
1916 if(drv < _max_drive) {
1917 disk[drv]->drive_type = type;
1921 uint8_t MB8877::get_drive_type(int drv)
1923 if(drv < _max_drive) {
1924 return disk[drv]->drive_type;
1926 return DRIVE_TYPE_UNK;
1929 void MB8877::set_drive_rpm(int drv, int rpm)
1931 if(drv < _max_drive) {
1932 disk[drv]->drive_rpm = rpm;
1936 void MB8877::set_drive_mfm(int drv, bool mfm)
1938 if(drv < _max_drive) {
1939 disk[drv]->drive_mfm = mfm;
1943 void MB8877::set_track_size(int drv, int size)
1945 if(drv < _max_drive) {
1946 disk[drv]->track_size = size;
1950 uint8_t MB8877::fdc_status()
1952 // for each virtual machines
1953 //#if defined(_FMR50) || defined(_FMR60)
1954 if(type_fmr50 || type_fmr60) {
1955 return disk[drvreg]->inserted ? 2 : 0;
1962 void MB8877::update_config()
1964 if(d_noise_seek != NULL) {
1965 d_noise_seek->set_mute(!config.sound_noise_fdd);
1967 if(d_noise_head_down != NULL) {
1968 d_noise_head_down->set_mute(!config.sound_noise_fdd);
1970 if(d_noise_head_up != NULL) {
1971 d_noise_head_up->set_mute(!config.sound_noise_fdd);
1973 fdc_debug_log = config.special_debug_fdc;
1976 #define STATE_VERSION 7
1978 void MB8877::save_state(FILEIO* state_fio)
1980 state_fio->FputUint32(STATE_VERSION);
1981 state_fio->FputInt32(this_device_id);
1983 state_fio->Fwrite(fdc, sizeof(fdc), 1);
1984 for(int i = 0; i < _max_drive; i++) {
1985 disk[i]->save_state(state_fio);
1987 state_fio->FputUint8(status);
1988 state_fio->FputUint8(status_tmp);
1989 state_fio->FputUint8(cmdreg);
1990 state_fio->FputUint8(cmdreg_tmp);
1991 state_fio->FputUint8(trkreg);
1992 state_fio->FputUint8(secreg);
1993 state_fio->FputUint8(datareg);
1994 state_fio->FputUint8(drvreg);
1995 state_fio->FputUint8(sidereg);
1996 state_fio->FputUint8(cmdtype);
1997 state_fio->Fwrite(register_id, sizeof(register_id), 1);
1998 state_fio->FputBool(now_search);
1999 state_fio->FputBool(now_seek);
2000 state_fio->FputBool(sector_changed);
2001 state_fio->FputInt32(no_command);
2002 state_fio->FputInt32(seektrk);
2003 state_fio->FputBool(seekvct);
2004 state_fio->FputBool(motor_on);
2005 state_fio->FputBool(drive_sel);
2006 state_fio->FputUint32(prev_drq_clock);
2007 state_fio->FputUint32(seekend_clock);
2010 bool MB8877::load_state(FILEIO* state_fio)
2012 if(state_fio->FgetUint32() != STATE_VERSION) {
2015 if(state_fio->FgetInt32() != this_device_id) {
2018 state_fio->Fread(fdc, sizeof(fdc), 1);
2019 for(int i = 0; i < _max_drive; i++) {
2020 if(!disk[i]->load_state(state_fio)) {
2024 status = state_fio->FgetUint8();
2025 status_tmp = state_fio->FgetUint8();
2026 cmdreg = state_fio->FgetUint8();
2027 cmdreg_tmp = state_fio->FgetUint8();
2028 trkreg = state_fio->FgetUint8();
2029 secreg = state_fio->FgetUint8();
2030 datareg = state_fio->FgetUint8();
2031 drvreg = state_fio->FgetUint8();
2032 sidereg = state_fio->FgetUint8();
2033 cmdtype = state_fio->FgetUint8();
2034 state_fio->Fread(register_id, sizeof(register_id), 1);
2035 now_search = state_fio->FgetBool();
2036 now_seek = state_fio->FgetBool();
2037 sector_changed = state_fio->FgetBool();
2038 no_command = state_fio->FgetInt32();
2039 seektrk = state_fio->FgetInt32();
2040 seekvct = state_fio->FgetBool();
2041 motor_on = state_fio->FgetBool();
2042 drive_sel = state_fio->FgetBool();
2043 prev_drq_clock = state_fio->FgetUint32();
2044 seekend_clock = state_fio->FgetUint32();
2046 fdc_debug_log = config.special_debug_fdc;