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"));
172 _drive_mask = _max_drive - 1;
173 for(int i = 0; i < _max_drive; i++) {
174 disk[i] = new DISK(emu);
175 disk[i]->set_device_name(_T("%s/Disk #%d"), this_device_name, i + 1);
178 if(osd->check_feature(_T("MB8877_NO_BUSY_AFTER_SEEK"))) {
179 mb8877_no_busy_after_seek = true;
181 //if(osd->check_feature(_T("_FDC_DEBUG_LOG"))) {
182 // fdc_debug_log = true;
184 fdc_debug_log = config.special_debug_fdc;
186 if(osd->check_feature(_T("HAS_MB8866"))) {
188 invert_registers = true;
189 set_device_name(_T("MB8866 FDC"));
191 if(osd->check_feature(_T("HAS_MB8876"))) {
192 invert_registers = true;
193 set_device_name(_T("MB8876 FDC"));
194 } else if(osd->check_feature(_T("HAS_MB89311"))) {
196 set_device_name(_T("MB89311 FDC"));
198 set_device_name(_T("MB8877 FDC"));
200 if(osd->check_feature(_T("_X1")) || osd->check_feature(_T("_X1TWIN")) ||
201 osd->check_feature(_T("_X1TURBO")) || osd->check_feature(_T("_X1TURBOZ"))) {
204 if(osd->check_feature(_T("_FM7")) || osd->check_feature(_T("_FM8")) ||
205 osd->check_feature(_T("_FM77_VARIANTS")) || osd->check_feature(_T("_FM77AV_VARIANTS"))) {
207 if(osd->check_feature(_T("_FM77AV40")) || osd->check_feature(_T("_FM77AV40EX")) ||
208 osd->check_feature(_T("_FM77AV40SX")) ||
209 osd->check_feature(_T("_FM77AV20")) || osd->check_feature(_T("_FM77AV20EX"))) {
210 type_fm77av_2dd = true;
213 if(osd->check_feature(_T("_FMR50"))) {
216 if(osd->check_feature(_T("_FMR60"))) {
221 if(d_noise_seek != NULL) {
222 d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)"));
223 if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) {
224 if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) {
225 d_noise_seek->load_wav_file(_T("SEEK.WAV"));
228 d_noise_seek->set_mute(!config.sound_noise_fdd);
230 if(d_noise_head_down != NULL) {
231 d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)"));
232 d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV"));
233 d_noise_head_down->set_mute(!config.sound_noise_fdd);
235 if(d_noise_head_up != NULL) {
236 d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)"));
237 d_noise_head_up->load_wav_file(_T("HEADUP.WAV"));
238 d_noise_head_up->set_mute(!config.sound_noise_fdd);
242 memset(fdc, 0, sizeof(fdc));
243 for(int i = 0; i < 16; i++) {
244 fdc[i].track = 40; // OK?
245 fdc[i].count_immediate = false;
250 status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
252 prev_drq_clock = seekend_clock = 0;
255 void MB8877::release()
257 // release d88 handler
258 for(int i = 0; i < _max_drive; i++) {
268 for(int i = 0; i < _max_drive; i++) {
269 fdc[i].track = 0; // Not Hold to track 20180506 K.O
271 fdc[i].access = false;
273 for(int i = 0; i < (int)array_length(register_id); i++) {
276 now_search = now_seek = drive_sel = false;
281 extended_mode = true;
283 extended_mode = false;
288 void MB8877::write_io8(uint32_t addr, uint32_t data)
296 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
297 if(invert_registers) {
298 cmdreg = (~data) & 0xff;
309 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
310 if(invert_registers) {
311 trkreg = (~data) & 0xff;
317 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
318 // track reg is written after command starts
319 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
326 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
327 if(invert_registers) {
328 secreg = (~data) & 0xff;
334 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
335 // sector reg is written after command starts
336 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
343 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
344 if(invert_registers) {
345 datareg = (~data) & 0xff;
351 ready = ((status & FDC_ST_DRQ) && !now_search);
353 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
355 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
356 if(!motor_on) ready = false;
358 if(!fdc[drvreg].head_load) ready = false;
362 if(!motor_on) ready = false;
363 if(!fdc[drvreg].head_load) ready = false;
365 // if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
367 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
368 // write or multisector write
369 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
370 if(!disk[drvreg]->write_protected) {
371 if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
372 disk[drvreg]->sector[fdc[drvreg].index] = datareg;
373 sector_changed = true;
376 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
378 status |= FDC_ST_WRITEFAULT;
379 status &= ~FDC_ST_BUSY;
383 //fdc[drvreg].index++;
385 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
386 if(cmdtype == FDC_CMD_WR_SEC) {
388 //#ifdef _FDC_DEBUG_LOG
389 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
391 status &= ~FDC_ST_BUSY;
396 //#ifdef _FDC_DEBUG_LOG
397 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
399 // 2HD: 360rpm, 10410bytes/track -> 0.06246bytes/us
400 register_my_event(EVENT_MULTI1, 30); // 0.06246bytes/us * 30us = 1.8738bytes < GAP3
401 register_my_event(EVENT_MULTI2, 60); // 0.06246bytes/us * 60us = 3.7476bytes < GAP3
403 sector_changed = false;
404 } else if(status & FDC_ST_DRQ) {
405 if(fdc[drvreg].index == 0) {
406 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
408 register_drq_event(1);
410 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
412 status &= ~FDC_ST_DRQ;
413 } else if(cmdtype == FDC_CMD_WR_TRK) {
415 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
416 if(!disk[drvreg]->write_protected) {
417 if(fdc[drvreg].index == 0) {
418 disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
419 fdc[drvreg].id_written = false;
420 fdc[drvreg].side = sidereg;
421 fdc[drvreg].side_changed = false;
423 if(fdc[drvreg].side != sidereg) {
424 fdc[drvreg].side_changed = true;
426 if(fdc[drvreg].side_changed) {
427 // abort write track because disk side is changed
428 } else if(datareg == 0xf5) {
429 // write a1h in missing clock
430 } else if(datareg == 0xf6) {
431 // write c2h in missing clock
432 } else if(datareg == 0xf7) {
434 if(!fdc[drvreg].id_written) {
435 // insert new sector with data crc error
437 uint8_t c = 0, h = 0, r = 0, n = 0;
438 fdc[drvreg].id_written = true;
439 fdc[drvreg].sector_found = false;
440 if(fdc[drvreg].index >= 4) {
441 c = disk[drvreg]->track[fdc[drvreg].index - 4];
442 h = disk[drvreg]->track[fdc[drvreg].index - 3];
443 r = disk[drvreg]->track[fdc[drvreg].index - 2];
444 n = disk[drvreg]->track[fdc[drvreg].index - 1];
446 fdc[drvreg].sector_length = 0x80 << (n & 3);
447 fdc[drvreg].sector_index = 0;
448 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
449 } else if(fdc[drvreg].sector_found) {
450 // clear data crc error if all sector data are written
451 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
452 disk[drvreg]->set_data_crc_error(false);
454 fdc[drvreg].id_written = false;
456 // data mark of current sector is not written
457 disk[drvreg]->set_data_mark_missing();
460 } else if(fdc[drvreg].id_written) {
461 if(fdc[drvreg].sector_found) {
463 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
464 disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
466 fdc[drvreg].sector_index++;
467 } else if(datareg == 0xf8 || datareg == 0xfb) {
469 disk[drvreg]->set_deleted(datareg == 0xf8);
470 fdc[drvreg].sector_found = true;
473 disk[drvreg]->track[fdc[drvreg].index] = datareg;
475 status |= FDC_ST_WRITEFAULT;
476 status &= ~FDC_ST_BUSY;
477 status &= ~FDC_ST_DRQ;
481 //fdc[drvreg].index++;
483 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
484 if(!disk[drvreg]->write_protected) {
485 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
486 // data mark of last sector is not written
487 disk[drvreg]->set_data_mark_missing();
489 disk[drvreg]->sync_buffer();
491 status &= ~FDC_ST_BUSY;
494 } else if(status & FDC_ST_DRQ) {
495 if(fdc[drvreg].index == 0) {
496 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
498 register_drq_event(1);
500 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
502 status &= ~FDC_ST_DRQ;
504 if(!(status & FDC_ST_DRQ)) {
505 cancel_my_event(EVENT_LOST);
507 fdc[drvreg].access = true;
514 uint32_t MB8877::read_io8(uint32_t addr)
517 bool not_ready = false;
527 // disk not inserted, motor stop
528 not_ready = !disk[drvreg]->inserted;
529 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
531 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
532 if(!motor_on) not_ready = true;
537 if(!motor_on) not_ready = true;
539 // if(!disk[drvreg]->inserted || !motor_on) {
541 status |= FDC_ST_NOTREADY;
543 status &= ~FDC_ST_NOTREADY;
546 if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
547 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
548 status |= FDC_ST_WRITEP;
550 status &= ~FDC_ST_WRITEP;
553 status &= ~FDC_ST_WRITEP;
555 // track0, index hole
556 if(cmdtype == FDC_CMD_TYPE1) {
557 if(fdc[drvreg].track == 0) {
558 status |= FDC_ST_TRACK00;
560 status &= ~FDC_ST_TRACK00;
562 // index hole signal width is 5msec (thanks Mr.Sato)
563 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
564 status |= FDC_ST_INDEX;
566 status &= ~FDC_ST_INDEX;
569 // show busy a moment
571 if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
572 status &= ~FDC_ST_BUSY;
573 //#ifdef MB8877_NO_BUSY_AFTER_SEEK
574 if(mb8877_no_busy_after_seek) {
575 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
577 if(disk[0]->is_special_disk != SPECIAL_DISK_FM7_XANADU2_D) {
590 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
591 // MZ-2000 HuBASIC invites NOT READY status
592 if(++no_command == 16) {
593 val |= FDC_ST_NOTREADY;
600 if(!(status & FDC_ST_DRQ)) {
603 if(!(status & FDC_ST_BUSY)) {
606 //#ifdef _FDC_DEBUG_LOG
607 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
609 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
610 if(invert_registers) {
611 return (~val) & 0xff;
619 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
620 if(invert_registers) {
621 return (~trkreg) & 0xff;
629 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
630 if(invert_registers) {
631 return (~secreg) & 0xff;
639 ready = ((status & FDC_ST_DRQ) && !now_search);
640 //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
642 if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
643 if(!motor_on) ready = false;
645 if(!fdc[drvreg].head_load) ready = false;
649 if(!motor_on) ready = false;
650 if(!fdc[drvreg].head_load) ready = false;
652 // if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
654 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
655 // read or multisector read
656 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
657 datareg = disk[drvreg]->sector[fdc[drvreg].index];
659 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) { // Reading complete this sector.
661 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
664 //#ifdef _FDC_DEBUG_LOG
665 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
667 status |= FDC_ST_CRCERR;
668 status &= ~FDC_ST_BUSY;
672 register_my_event_with_check(EVENT_CRC_ERROR, disk[drvreg]->get_usec_per_bytes(3));
674 } else if(cmdtype == FDC_CMD_RD_SEC) {
677 //#ifdef _FDC_DEBUG_LOG
678 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
680 status &= ~FDC_ST_BUSY;
684 register_my_event_with_check(EVENT_END_READ_SECTOR, disk[drvreg]->get_usec_per_bytes(3));
688 //#ifdef _FDC_DEBUG_LOG
689 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
691 register_my_event(EVENT_MULTI1, 30);
692 register_my_event(EVENT_MULTI2, 60);
694 } else { // Still data remain.
695 register_drq_event(1);
696 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
698 status &= ~FDC_ST_DRQ;
699 } else if(cmdtype == FDC_CMD_RD_ADDR) {
701 if(fdc[drvreg].index < 6) {
702 datareg = disk[drvreg]->id[fdc[drvreg].index];
703 //fdc[drvreg].index++;
705 if((fdc[drvreg].index + 1) >= 6) {
706 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
708 status |= FDC_ST_CRCERR;
710 status &= ~FDC_ST_BUSY;
713 //#ifdef _FDC_DEBUG_LOG
714 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
717 register_drq_event(1);
718 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
720 status &= ~FDC_ST_DRQ;
721 } else if(cmdtype == FDC_CMD_RD_TRK) {
723 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
724 datareg = disk[drvreg]->track[fdc[drvreg].index];
725 //fdc[drvreg].index++;
727 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
728 //#ifdef _FDC_DEBUG_LOG
729 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF TRACK\n"));
731 status &= ~FDC_ST_BUSY;
732 status |= FDC_ST_LOSTDATA;
736 register_drq_event(1);
737 if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
739 status &= ~FDC_ST_DRQ;
741 if(!(status & FDC_ST_DRQ)) {
742 cancel_my_event(EVENT_LOST);
744 fdc[drvreg].access = true;
747 //#ifdef _FDC_DEBUG_LOG
748 if(fdc_debug_log) this->force_out_debug_log(_T("FDC\tDATA=%2x\n"), datareg); // emu->force_out_debug_log()
750 //#if defined(HAS_MB8866) || defined(HAS_MB8876)
751 if(invert_registers) {
752 return (~datareg) & 0xff;
762 void MB8877::write_dma_io8(uint32_t addr, uint32_t data)
767 uint32_t MB8877::read_dma_io8(uint32_t addr)
772 void MB8877::write_signal(int id, uint32_t data, uint32_t mask)
774 if(id == SIG_MB8877_DRIVEREG) {
775 drvreg = data & _drive_mask;
777 seekend_clock = get_current_clock();
778 } else if(id == SIG_MB8877_SIDEREG) {
779 sidereg = (data & mask) ? 1 : 0;
780 } else if(id == SIG_MB8877_MOTOR) {
781 motor_on = ((data & mask) != 0);
785 uint32_t MB8877::read_signal(int ch)
787 if(ch == SIG_MB8877_DRIVEREG) {
788 return drvreg & _drive_mask;
789 } else if(ch == SIG_MB8877_SIDEREG) {
791 } else if(ch == SIG_MB8877_MOTOR) {
792 return motor_on ? 1 : 0;
797 for(int i = 0; i < _max_drive; i++) {
801 fdc[i].access = false;
809 void MB8877::event_callback(int event_id, int err)
811 int event = event_id >> 8;
812 int cmd = event_id & 0xff;
813 //bool need_after_irq = false;
814 register_id[event] = -1;
816 // cancel event if the command is finished or other command is executed
818 if(event == EVENT_SEEK || event == EVENT_SEEKEND_VERIFY) {
820 } else if(event == EVENT_SEARCH) {
828 //#ifdef _FDC_DEBUG_LOG
829 //this->out_debug_log(_T("FDC\tSEEK START\n"));
831 if(seektrk > fdc[drvreg].track) {
833 if(d_noise_seek != NULL) d_noise_seek->play();
834 //need_after_irq = true;
835 } else if(seektrk < fdc[drvreg].track) {
837 if(d_noise_seek != NULL) d_noise_seek->play();
838 //need_after_irq = true;
840 // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
841 if((((cmdreg & 0x10) != 0) && (cmdreg >= 0x20)) || ((cmdreg & 0xf0) == 0)) {
842 trkreg = fdc[drvreg].track;
844 if((uint8_t)seektrk != (uint8_t)fdc[drvreg].track) {
845 //if(need_after_irq) {
847 //set_irq(true); // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
849 register_seek_event();
853 // Verify before execute command.
854 // Port from XM7.Thanks to Ryu Takegami.
857 if(trkreg != fdc[drvreg].track) {
858 status |= FDC_ST_SEEKERR;
859 trkreg = fdc[drvreg].track; // Reload track register when verify: Really OK? 20180224 K.O
863 seekend_clock = get_current_clock();
867 if((cmdreg & 0xf4) == 0x44) {
871 } else if((cmdreg & 0xf4) == 0x64) {
882 status_tmp |= search_track();
885 if(status_tmp & FDC_ST_SEEKERR) {
886 time = get_usec_to_detect_index_hole(5, true);
888 time = get_usec_to_next_trans_pos(true);
891 // Verify wait is shorter than above at least FM-7 series's MB8877. 20180223 K.O.
892 // Thanks to Ryu Takegami.
893 time = (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ? 10000.0 : 20000.0;
895 // 20180128 K.O If seek completed, delay to make IRQ/DRQ at SEEKEND_VERIFY.
896 register_my_event(EVENT_SEEKEND_VERIFY, time);
899 // case EVENT_SEEKEND: // 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
906 case EVENT_SEEKEND_VERIFY: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
908 status &= (uint8_t)(~FDC_ST_BUSY); // 20180118 K.O Turn off BUSY flag.
910 set_irq(true); // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
911 //#ifdef _FDC_DEBUG_LOG
912 //this->out_debug_log(_T("FDC\tSEEK END\n"));
917 if(status_tmp & FDC_ST_RECNFND) {
918 //#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
920 // for SHARP X1 Batten Tanuki
921 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
922 status_tmp &= ~FDC_ST_RECNFND;
926 //#ifdef _FDC_DEBUG_LOG
927 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH NG\n"));
929 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
933 } else if(status_tmp & FDC_ST_WRITEFAULT) {
934 //#ifdef _FDC_DEBUG_LOG
935 if(fdc_debug_log) this->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
937 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
942 status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
943 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
944 register_lost_event(8);
945 } else if(cmdtype == FDC_CMD_WR_TRK) {
946 register_lost_event(3);
948 register_lost_event(1);
950 fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
951 fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
954 if(fdc_debug_log) this->out_debug_log("DRQ ON@SEARCH: %d\n", prev_drq_clock);
955 //#ifdef _FDC_DEBUG_LOG
956 if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH OK\n"));
961 if(status & FDC_ST_BUSY) {
962 status |= FDC_ST_DRQ;
963 register_lost_event(1);
964 if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
965 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
967 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
969 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
970 cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
971 cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK ||
972 cmdtype == FDC_CMD_RD_ADDR) {
973 if(!(fdc[drvreg].count_immediate)) fdc[drvreg].index++;
975 fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
978 if(fdc_debug_log) this->out_debug_log("DRQ ON@DRQ: %d\n", prev_drq_clock);
979 //#ifdef _FDC_DEBUG_LOG
980 //this->out_debug_log(_T("FDC\tDRQ!\n"));
988 if(cmdtype == FDC_CMD_RD_MSEC) {
990 } else if(cmdtype == FDC_CMD_WR_MSEC) {
991 cmd_writedata(false);
994 case EVENT_CRC_ERROR:
995 //#ifdef _FDC_DEBUG_LOG
996 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
998 status |= FDC_ST_CRCERR;
999 status &= ~FDC_ST_BUSY;
1003 case EVENT_END_READ_SECTOR:
1004 //#ifdef _FDC_DEBUG_LOG
1005 if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
1007 status &= ~FDC_ST_BUSY;
1012 if(status & FDC_ST_BUSY) {
1013 //#ifdef _FDC_DEBUG_LOG
1014 if(fdc_debug_log) this->out_debug_log(_T("FDC\tDATA LOST\n"));
1016 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
1017 if(fdc[drvreg].index == 0) { // HEAD of REGION
1019 //if((status & FDC_ST_DRQ) != 0) { // 20180130 FORCE DOWN DRQ ON LOST-DATA.
1020 status |= FDC_ST_LOSTDATA;
1021 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1025 } else { // STILL WRITING
1027 //if((status & FDC_ST_DRQ) != 0) {
1028 status |= FDC_ST_LOSTDATA;
1029 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1036 //if((status & FDC_ST_DRQ) != 0) {
1037 status |= FDC_ST_LOSTDATA;
1038 status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
1044 status |= FDC_ST_LOSTDATA;
1050 // ----------------------------------------------------------------------------
1052 // ----------------------------------------------------------------------------
1055 void MB8877::process_cmd()
1060 //#ifdef HAS_MB89311
1061 // MB89311 mode commands
1063 if(cmdreg == 0xfc) {
1064 // delay (may be only in extended mode)
1065 //#ifdef _FDC_DEBUG_LOG
1066 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);
1068 cmdtype = status = 0;
1070 } else if(cmdreg == 0xfd) {
1072 //#ifdef _FDC_DEBUG_LOG
1073 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);
1075 cmdtype = status = 0;
1077 } else if(cmdreg == 0xfe) {
1079 //#ifdef _FDC_DEBUG_LOG
1080 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);
1082 extended_mode = !extended_mode;
1083 cmdtype = status = 0;
1085 } else if(cmdreg == 0xff) {
1086 // reset (may be only in extended mode)
1087 //#ifdef _FDC_DEBUG_LOG
1088 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);
1091 extended_mode = true;
1093 } else if(extended_mode) {
1095 if((cmdreg & 0xeb) == 0x21) {
1096 //#ifdef _FDC_DEBUG_LOG
1097 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);
1101 } else if((cmdreg & 0xeb) == 0x22) {
1102 //#ifdef _FDC_DEBUG_LOG
1103 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);
1108 } else if((cmdreg & 0xf4) == 0x44) {
1110 //#ifdef _FDC_DEBUG_LOG
1111 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);
1115 } else if((cmdreg & 0xf4) == 0x64) {
1117 //#ifdef _FDC_DEBUG_LOG
1118 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);
1123 } else if((cmdreg & 0xfb) == 0xf1) {
1125 //#ifdef _FDC_DEBUG_LOG
1126 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);
1135 // MB8877 mode commands
1138 memset(tmps, 0x00, sizeof(tmps));
1139 get_debug_regs_info(tmps, sizeof(tmps));
1140 if(fdc_debug_log) this->out_debug_log(_T("%s"), tmps);
1142 switch(cmdreg & 0xf8) {
1144 case 0x00: case 0x08:
1146 update_head_flag(drvreg, (cmdreg & 8) != 0);
1148 case 0x10: case 0x18:
1150 update_head_flag(drvreg, (cmdreg & 8) != 0);
1152 case 0x20: case 0x28:
1153 case 0x30: case 0x38:
1155 update_head_flag(drvreg, (cmdreg & 8) != 0);
1157 case 0x40: case 0x48:
1158 case 0x50: case 0x58:
1160 update_head_flag(drvreg, (cmdreg & 8) != 0);
1162 case 0x60: case 0x68:
1163 case 0x70: case 0x78:
1165 update_head_flag(drvreg, (cmdreg & 8) != 0);
1168 case 0x80: case 0x88:
1169 case 0x90: case 0x98:
1171 update_head_flag(drvreg, true);
1173 case 0xa0:case 0xa8:
1174 case 0xb0: case 0xb8:
1175 cmd_writedata(true);
1176 update_head_flag(drvreg, true);
1181 update_head_flag(drvreg, true);
1185 update_head_flag(drvreg, true);
1189 update_head_flag(drvreg, true);
1192 case 0xd0: case 0xd8:
1201 void MB8877::cmd_restore()
1204 cmdtype = FDC_CMD_TYPE1;
1205 if(!check_drive()) {
1208 if((cmdreg & 0x08) != 0) { // Head engage
1209 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1211 status = FDC_ST_BUSY;
1216 if(fdc[drvreg].track < 0) {
1217 fdc[drvreg].track = 0;
1219 } else if(fdc[drvreg].track >= 80) {
1220 fdc[drvreg].track = 80;
1223 trkreg = fdc[drvreg].track;
1230 register_seek_event();
1233 void MB8877::cmd_seek()
1236 cmdtype = FDC_CMD_TYPE1;
1237 if(!check_drive()) {
1240 if((cmdreg & 0x08) != 0) { // Head engage
1241 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1243 status = FDC_ST_BUSY;
1246 // revert below 20180225 K.O
1247 //seektrk = (uint8_t)(fdc[drvreg].track + datareg - trkreg); // Seek target is differ when drive's track != trkreg.Thanks to Haserin and Ryu Takegami.
1248 seektrk = (int)((int8_t)datareg);
1249 //#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
1250 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1251 seektrk = disk[drvreg]->get_max_tracks() - 1;
1252 } else if(seektrk < 0) {
1256 seekvct = !(seektrk > fdc[drvreg].track);
1257 // Update track register by data register.Thanks to Ryu Takegami. 20180224 K.O
1261 register_seek_event();
1264 void MB8877::cmd_step()
1274 void MB8877::cmd_stepin()
1277 cmdtype = FDC_CMD_TYPE1;
1278 if(!check_drive()) {
1281 if((cmdreg & 0x08) != 0) { // Head engage
1282 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1284 status = FDC_ST_BUSY;
1287 // Verify before execute command.
1288 // Port from XM7.Thanks to Ryu Takegami.
1292 if(trkreg != fdc[drvreg].track) {
1293 status |= FDC_ST_SEEKERR;
1294 // trkreg = fdc[drvreg].track;
1298 seektrk = fdc[drvreg].track + 1;
1299 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1300 seektrk = disk[drvreg]->get_max_tracks() - 1;
1301 } else if(seektrk < 0) {
1307 register_seek_event();
1310 void MB8877::cmd_stepout()
1313 cmdtype = FDC_CMD_TYPE1;
1314 if(!check_drive()) {
1317 if((cmdreg & 0x08) != 0) { // Head engage
1318 status = FDC_ST_HEADENG | FDC_ST_BUSY;
1320 status = FDC_ST_BUSY;
1323 // Verify before execute command.
1324 // Port from XM7.Thanks to Ryu Takegami.
1328 if(trkreg != fdc[drvreg].track) {
1329 status |= FDC_ST_SEEKERR;
1330 // trkreg = fdc[drvreg].track;
1334 seektrk = fdc[drvreg].track - 1;
1335 if(seektrk >= disk[drvreg]->get_max_tracks()) {
1336 seektrk = disk[drvreg]->get_max_tracks() - 1;
1337 } else if(seektrk < 0) {
1341 register_seek_event();
1344 void MB8877::cmd_readdata(bool first_sector)
1347 cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
1348 if(!check_drive2()) {
1351 status = FDC_ST_BUSY;
1352 status_tmp = search_sector();
1356 if(status_tmp & FDC_ST_RECNFND) {
1357 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1359 time = get_usec_to_start_trans(first_sector);
1361 register_my_event(EVENT_SEARCH, time);
1362 cancel_my_event(EVENT_LOST);
1365 void MB8877::cmd_writedata(bool first_sector)
1367 // type-2 write data
1368 cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
1369 if(!check_drive2()) {
1372 status = FDC_ST_BUSY;
1373 status_tmp = search_sector() & ~FDC_ST_RECTYPE;
1375 sector_changed = false;
1378 if(status_tmp & FDC_ST_RECNFND) {
1379 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
1380 } else if(status & FDC_ST_WRITEFAULT) {
1381 time = (cmdreg & 4) ? DELAY_TIME : 1;
1383 time = get_usec_to_start_trans(first_sector);
1385 register_my_event(EVENT_SEARCH, time);
1386 cancel_my_event(EVENT_LOST);
1389 void MB8877::cmd_readaddr()
1391 // type-3 read address
1392 cmdtype = FDC_CMD_RD_ADDR;
1393 if(!check_drive2()) {
1396 status = FDC_ST_BUSY;
1397 status_tmp = search_addr();
1401 if(status_tmp & FDC_ST_RECNFND) {
1402 time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
1404 time = get_usec_to_start_trans(true);
1406 register_my_event(EVENT_SEARCH, time);
1407 cancel_my_event(EVENT_LOST);
1410 void MB8877::cmd_readtrack()
1412 // type-3 read track
1413 cmdtype = FDC_CMD_RD_TRK;
1414 if(!check_drive2()) {
1417 status = FDC_ST_BUSY;
1420 disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1421 fdc[drvreg].index = 0;
1424 fdc[drvreg].next_trans_position = 1;
1425 double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
1426 register_my_event(EVENT_SEARCH, time);
1427 cancel_my_event(EVENT_LOST);
1430 void MB8877::cmd_writetrack()
1432 // type-3 write track
1433 cmdtype = FDC_CMD_WR_TRK;
1434 if(!check_drive2()) {
1437 status = FDC_ST_BUSY;
1440 fdc[drvreg].index = 0;
1441 fdc[drvreg].id_written = false;
1445 if(disk[drvreg]->write_protected) {
1446 status_tmp = FDC_ST_WRITEFAULT;
1447 time = (cmdreg & 4) ? DELAY_TIME : 1;
1450 // wait 15msec before raise first drq
1451 fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1454 // raise first drq soon
1455 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1456 time = disk[drvreg]->get_usec_per_bytes(1);
1458 // wait at least 3bytes before check index hole and raise second drq
1459 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1460 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1461 fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1464 register_my_event(EVENT_SEARCH, time);
1465 cancel_my_event(EVENT_LOST);
1468 //#ifdef HAS_MB89311
1469 void MB8877::cmd_format()
1472 // type-3 format (FIXME: need to implement)
1473 cmdtype = FDC_CMD_WR_TRK;
1474 status = FDC_ST_BUSY;
1477 fdc[drvreg].index = 0;
1478 fdc[drvreg].id_written = false;
1481 status_tmp = FDC_ST_WRITEFAULT;
1482 double time = (cmdreg & 4) ? DELAY_TIME : 1;
1484 register_my_event(EVENT_SEARCH, time);
1485 cancel_my_event(EVENT_LOST);
1490 void MB8877::cmd_forceint()
1492 // type-4 force interrupt
1493 bool ready = (cmdtype == 0);
1494 bool is_busy = ((status & FDC_ST_BUSY) != 0);
1495 bool now_seek_bak = now_seek;
1496 bool is_type1 = (cmdtype == FDC_CMD_TYPE1);
1497 bool is_read = ((cmdtype == FDC_CMD_RD_SEC) || (cmdtype == FDC_CMD_RD_MSEC) || \
1498 (cmdtype == FDC_CMD_RD_ADDR) || (cmdtype == FDC_CMD_RD_TRK));
1499 bool is_trkwrite = (cmdtype == FDC_CMD_WR_TRK);
1501 if(cmdtype == FDC_CMD_TYPE1) {
1502 // abort restore/seek/step command
1504 if(seektrk > fdc[drvreg].track) {
1505 if((fdc[drvreg].track < 0xff) && (fdc[drvreg].track >= 0)) fdc[drvreg].track++;
1506 } else if(seektrk < fdc[drvreg].track) {
1507 if((fdc[drvreg].track <= 0xff) && (fdc[drvreg].track > 0)) fdc[drvreg].track--;
1509 // Not update track register if "SEEK" command.Thanks to Haserin. 20180224 K.O.
1510 if((((cmdreg_tmp & 0x10) != 0) && (cmdreg_tmp >= 0x20)) || ((cmdreg_tmp & 0xf0) == 0)) {
1511 trkreg = fdc[drvreg].track;
1514 } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1515 // abort write sector command
1516 if(sector_changed) {
1517 disk[drvreg]->set_data_crc_error(false);
1519 } else if(cmdtype == FDC_CMD_WR_TRK) {
1520 // abort write track command
1521 if(!disk[drvreg]->write_protected) {
1522 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1523 // data mark of last sector is not written
1524 disk[drvreg]->set_data_mark_missing();
1526 disk[drvreg]->sync_buffer();
1529 now_search = now_seek = sector_changed = false;
1531 //#ifdef HAS_MB89311
1532 if((cmdreg == 0xff) && (type_mb89311)) {
1534 cmdtype = FDC_CMD_TYPE1;
1535 status = FDC_ST_HEADENG;
1538 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1539 cmdtype = FDC_CMD_TYPE1;
1540 if(!type_fm7) status = FDC_ST_HEADENG; // Hack for FUKUALL.d77 .
1542 status &= ~FDC_ST_BUSY;
1543 if((now_seek_bak) || (ready)) {// Refer from XM7, is this write implement? 20180210 K.O
1545 if(((ready) || !(is_busy)) && (!type_fm7)) status = FDC_ST_HEADENG;
1547 if(check_drive2()) {
1548 if(!(is_read) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEP;
1549 if((is_trkwrite) && (disk[drvreg]->write_protected)) status |= FDC_ST_WRITEFAULT;
1551 if(fdc[drvreg].track == 0) status |= FDC_ST_TRACK00;
1552 if(get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
1553 status |= FDC_ST_INDEX;
1558 // force interrupt if bit0-bit3 is high
1562 //#ifdef HAS_MB89311
1566 cancel_my_event(EVENT_SEEK);
1567 cancel_my_event(EVENT_SEEKEND_VERIFY);
1568 cancel_my_event(EVENT_SEARCH);
1569 cancel_my_event(EVENT_DRQ);
1570 cancel_my_event(EVENT_MULTI1);
1571 cancel_my_event(EVENT_MULTI2);
1572 cancel_my_event(EVENT_LOST);
1575 void MB8877::update_head_flag(int drv, bool head_load)
1577 if(fdc[drv].head_load != head_load) {
1579 if(d_noise_head_down != NULL) d_noise_head_down->play();
1581 if(d_noise_head_up != NULL) d_noise_head_up->play();
1583 fdc[drv].head_load = head_load;
1587 // ----------------------------------------------------------------------------
1589 // ----------------------------------------------------------------------------
1591 uint8_t MB8877::search_track()
1594 int track = fdc[drvreg].track;
1596 if(!disk[drvreg]->get_track(track, sidereg)){
1597 return FDC_ST_SEEKERR;
1600 // verify track number
1601 if(disk[drvreg]->ignore_crc()) {
1602 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1603 disk[drvreg]->get_sector(-1, -1, i);
1604 if(disk[drvreg]->id[0] == trkreg) {
1605 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1606 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1611 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1612 disk[drvreg]->get_sector(-1, -1, i);
1613 if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1614 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1615 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1619 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1620 disk[drvreg]->get_sector(-1, -1, i);
1621 if(disk[drvreg]->id[0] == trkreg) {
1622 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1626 return FDC_ST_SEEKERR;
1629 uint8_t MB8877::search_sector()
1632 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1633 if(disk[drvreg]->write_protected) {
1634 return FDC_ST_WRITEFAULT;
1639 int track = fdc[drvreg].track;
1642 if(!disk[drvreg]->get_track(track, sidereg)) {
1643 return FDC_ST_RECNFND;
1646 // get current position
1647 int sector_num = disk[drvreg]->sector_num.sd;
1648 int position = get_cur_position();
1650 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1651 position -= disk[drvreg]->get_track_size();
1654 // first scanned sector
1655 int first_sector = 0;
1656 for(int i = 0; i < sector_num; i++) {
1657 if(position < disk[drvreg]->am1_position[i]) {
1664 for(int i = 0; i < sector_num; i++) {
1666 int index = (first_sector + i) % sector_num;
1667 disk[drvreg]->get_sector(-1, -1, index);
1670 if(disk[drvreg]->id[0] != trkreg) {
1673 //#if !defined(HAS_MB8866)
1675 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1680 if(disk[drvreg]->id[2] != secreg) {
1683 if(disk[drvreg]->sector_size.sd == 0) {
1686 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1688 disk[drvreg]->sector_size.sd = 0;
1689 return FDC_ST_RECNFND | FDC_ST_CRCERR;
1693 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1694 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
1695 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
1697 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
1699 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[index];
1700 fdc[drvreg].index = 0;
1701 //#ifdef _FDC_DEBUG_LOG
1702 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"),
1703 disk[drvreg]->sector_size.sd,
1704 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1705 disk[drvreg]->id[4], disk[drvreg]->id[5],
1706 disk[drvreg]->data_crc_error ? 1 : 0);
1708 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1712 disk[drvreg]->sector_size.sd = 0;
1713 return FDC_ST_RECNFND;
1716 uint8_t MB8877::search_addr()
1719 int track = fdc[drvreg].track;
1721 if(!disk[drvreg]->get_track(track, sidereg)) {
1722 return FDC_ST_RECNFND;
1725 // get current position
1726 int sector_num = disk[drvreg]->sector_num.sd;
1727 int position = get_cur_position();
1729 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1730 position -= disk[drvreg]->get_track_size();
1733 // first scanned sector
1734 int first_sector = 0;
1735 for(int i = 0; i < sector_num; i++) {
1736 if(position < disk[drvreg]->am1_position[i]) {
1743 if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1744 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
1745 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
1746 fdc[drvreg].index = 0;
1747 secreg = disk[drvreg]->id[0];
1752 disk[drvreg]->sector_size.sd = 0;
1753 return FDC_ST_RECNFND;
1756 // ----------------------------------------------------------------------------
1758 // ----------------------------------------------------------------------------
1760 int MB8877::get_cur_position()
1762 return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1765 double MB8877::get_usec_to_start_trans(bool first_sector)
1767 // get time from current position
1768 double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
1769 if(first_sector && time < 60000 - get_passed_usec(seekend_clock)) {
1770 time += disk[drvreg]->get_usec_per_track();
1775 double MB8877::get_usec_to_next_trans_pos(bool delay)
1777 int position = get_cur_position();
1779 if(disk[drvreg]->invalid_format) {
1780 // XXX: this track is invalid format and the calculated sector position may be incorrect.
1781 // so use the constant period
1783 } else if(/*disk[drvreg]->no_skew && */!disk[drvreg]->correct_timing()) {
1784 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1785 // so use the period to search the next sector from the current position
1786 int sector_num = disk[drvreg]->sector_num.sd;
1789 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1790 position -= disk[drvreg]->get_track_size();
1792 for(int i = 0; i < sector_num; i++) {
1793 if(position < disk[drvreg]->am1_position[i]) {
1794 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1795 bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
1797 bytes = (disk[drvreg]->data_position[i] + 1) - position;
1800 bytes += disk[drvreg]->get_track_size(); // to make sure
1806 return disk[drvreg]->get_usec_per_bytes(bytes);
1811 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1813 int bytes = fdc[drvreg].next_trans_position - position;
1814 if(fdc[drvreg].next_am1_position < position || bytes < 0) {
1815 bytes += disk[drvreg]->get_track_size();
1817 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1824 double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
1826 int position = get_cur_position();
1828 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1830 int bytes = disk[drvreg]->get_track_size() * count - position;
1832 bytes += disk[drvreg]->get_track_size();
1834 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1841 // ----------------------------------------------------------------------------
1843 // ----------------------------------------------------------------------------
1845 void MB8877::set_irq(bool val)
1847 write_signals(&outputs_irq, val ? 0xffffffff : 0);
1850 void MB8877::set_drq(bool val)
1852 write_signals(&outputs_drq, val ? 0xffffffff : 0);
1855 // ----------------------------------------------------------------------------
1857 // ----------------------------------------------------------------------------
1859 void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
1861 if(drv < _max_drive) {
1862 disk[drv]->open(file_path, bank);
1863 #if defined(_USE_QT)
1864 if((disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) || (config.disk_count_immediate[drv])) {
1866 if(disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) {
1868 fdc[drv].count_immediate = true;
1870 fdc[drv].count_immediate = false;
1875 void MB8877::close_disk(int drv)
1877 if(drv < _max_drive) {
1880 update_head_flag(drvreg, false);
1881 fdc[drv].count_immediate = false;
1885 bool MB8877::is_disk_inserted(int drv)
1887 if(drv < _max_drive) {
1888 return disk[drv]->inserted;
1893 void MB8877::is_disk_protected(int drv, bool value)
1895 if(drv < _max_drive) {
1896 disk[drv]->write_protected = value;
1900 bool MB8877::is_disk_protected(int drv)
1902 if(drv < _max_drive) {
1903 return disk[drv]->write_protected;
1908 uint8_t MB8877::media_type(int drv)
1910 if(drv < _max_drive) {
1911 if(disk[drv]->inserted) {
1912 return disk[drv]->media_type;
1915 return MEDIA_TYPE_UNK;
1918 void MB8877::set_drive_type(int drv, uint8_t type)
1920 if(drv < _max_drive) {
1921 disk[drv]->drive_type = type;
1925 uint8_t MB8877::get_drive_type(int drv)
1927 if(drv < _max_drive) {
1928 return disk[drv]->drive_type;
1930 return DRIVE_TYPE_UNK;
1933 void MB8877::set_drive_rpm(int drv, int rpm)
1935 if(drv < _max_drive) {
1936 disk[drv]->drive_rpm = rpm;
1940 void MB8877::set_drive_mfm(int drv, bool mfm)
1942 if(drv < _max_drive) {
1943 disk[drv]->drive_mfm = mfm;
1947 void MB8877::set_track_size(int drv, int size)
1949 if(drv < _max_drive) {
1950 disk[drv]->track_size = size;
1954 uint8_t MB8877::fdc_status()
1956 // for each virtual machines
1957 //#if defined(_FMR50) || defined(_FMR60)
1958 if(type_fmr50 || type_fmr60) {
1959 return disk[drvreg]->inserted ? 2 : 0;
1966 void MB8877::update_config()
1968 if(d_noise_seek != NULL) {
1969 d_noise_seek->set_mute(!config.sound_noise_fdd);
1971 if(d_noise_head_down != NULL) {
1972 d_noise_head_down->set_mute(!config.sound_noise_fdd);
1974 if(d_noise_head_up != NULL) {
1975 d_noise_head_up->set_mute(!config.sound_noise_fdd);
1977 fdc_debug_log = config.special_debug_fdc;
1980 //#ifdef USE_DEBUGGER
1981 static const _TCHAR *cmdstr[0x10] = {
1982 _T("RESTORE "), _T("SEEK "), _T("STEP "), _T("STEP "),
1983 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
1984 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
1985 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
1987 void MB8877::get_debug_regs_info(_TCHAR *buffer, size_t buffer_len)
1989 my_stprintf_s(buffer, buffer_len,
1990 _T("CMDREG=%02X (%s) DATAREG=%02X DRVREG=%02X TRKREG=%02X SIDEREG=%d SECREG=%02X\nUNIT: DRIVE=%d TRACK=%2d SIDE=%d SECTORS=%2d C=%02X H=%02X R=%02X N=%02X LENGTH=%d"),
1991 cmdreg, cmdstr[cmdreg >> 4], datareg, drvreg, trkreg, sidereg, secreg,
1992 drvreg, fdc[drvreg].track, sidereg, disk[drvreg]->sector_num.sd,
1993 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1994 disk[drvreg]->sector_size.sd);
1998 #define STATE_VERSION 7
2000 #include "../statesub.h"
2002 void MB8877::decl_state_fdc(int ch)
2004 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].track), ch);
2005 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].index), ch);
2006 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].access), ch);
2007 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].head_load), ch);
2008 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].id_written), ch);
2009 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].sector_found), ch);
2011 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].sector_length), ch);
2012 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].sector_index), ch);
2014 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].side), ch);
2015 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].side_changed), ch);
2017 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].cur_position), ch);
2018 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].next_trans_position), ch);
2020 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].bytes_before_2nd_drq), ch);
2021 DECL_STATE_ENTRY_INT32_MEMBER((fdc[ch].next_am1_position), ch);
2023 DECL_STATE_ENTRY_UINT32_MEMBER((fdc[ch].prev_clock), ch);
2024 DECL_STATE_ENTRY_BOOL_MEMBER((fdc[ch].count_immediate), ch);
2027 void MB8877::decl_state()
2029 enter_decl_state(STATE_VERSION);
2031 for(int ch = 0; ch < 16; ch++) {
2034 DECL_STATE_ENTRY_UINT8(status);
2035 DECL_STATE_ENTRY_UINT8(status_tmp);
2036 DECL_STATE_ENTRY_UINT8(cmdreg);
2037 DECL_STATE_ENTRY_UINT8(cmdreg_tmp);
2038 DECL_STATE_ENTRY_UINT8(trkreg);
2039 DECL_STATE_ENTRY_UINT8(secreg);
2040 DECL_STATE_ENTRY_UINT8(datareg);
2041 DECL_STATE_ENTRY_UINT8(drvreg);
2042 DECL_STATE_ENTRY_UINT8(sidereg);
2043 DECL_STATE_ENTRY_UINT8(cmdtype);
2044 DECL_STATE_ENTRY_1D_ARRAY(register_id, sizeof(register_id) / sizeof(int));
2045 DECL_STATE_ENTRY_BOOL(now_search);
2046 DECL_STATE_ENTRY_BOOL(now_seek);
2047 DECL_STATE_ENTRY_BOOL(sector_changed);
2048 DECL_STATE_ENTRY_INT32(no_command);
2049 DECL_STATE_ENTRY_INT32(seektrk);
2050 DECL_STATE_ENTRY_BOOL(seekvct);
2051 DECL_STATE_ENTRY_BOOL(motor_on);
2052 DECL_STATE_ENTRY_BOOL(drive_sel);
2053 DECL_STATE_ENTRY_UINT32(prev_drq_clock);
2054 DECL_STATE_ENTRY_UINT32(seekend_clock);
2056 for(int i = 0; i < _max_drive; i++) {
2057 disk[i]->decl_state(p_logger);
2061 void MB8877::save_state(FILEIO* state_fio)
2063 if(state_entry != NULL) {
2064 state_entry->save_state(state_fio);
2066 //state_fio->FputUint32(STATE_VERSION);
2067 //state_fio->FputInt32(this_device_id);
2069 //state_fio->Fwrite(fdc, sizeof(fdc), 1);
2070 for(int i = 0; i < _max_drive; i++) {
2071 disk[i]->save_state(state_fio);
2073 //state_fio->FputUint8(status);
2074 //state_fio->FputUint8(status_tmp);
2075 //state_fio->FputUint8(cmdreg);
2076 //state_fio->FputUint8(cmdreg_tmp);
2077 //state_fio->FputUint8(trkreg);
2078 //state_fio->FputUint8(secreg);
2079 //state_fio->FputUint8(datareg);
2080 //state_fio->FputUint8(drvreg);
2081 //state_fio->FputUint8(sidereg);
2082 //state_fio->FputUint8(cmdtype);
2083 //state_fio->Fwrite(register_id, sizeof(register_id), 1);
2084 //state_fio->FputBool(now_search);
2085 //state_fio->FputBool(now_seek);
2086 //state_fio->FputBool(sector_changed);
2087 //state_fio->FputInt32(no_command);
2088 //state_fio->FputInt32(seektrk);
2089 //state_fio->FputBool(seekvct);
2090 //state_fio->FputBool(motor_on);
2091 //state_fio->FputBool(drive_sel);
2092 //state_fio->FputUint32(prev_drq_clock);
2093 //state_fio->FputUint32(seekend_clock);
2096 bool MB8877::load_state(FILEIO* state_fio)
2099 if(state_entry != NULL) {
2100 mb = state_entry->load_state(state_fio);
2102 if(!mb) return false;
2103 //if(state_fio->FgetUint32() != STATE_VERSION) {
2106 //if(state_fio->FgetInt32() != this_device_id) {
2109 //state_fio->Fread(fdc, sizeof(fdc), 1);
2110 for(int i = 0; i < _max_drive; i++) {
2111 if(!disk[i]->load_state(state_fio)) {
2115 //status = state_fio->FgetUint8();
2116 //status_tmp = state_fio->FgetUint8();
2117 //cmdreg = state_fio->FgetUint8();
2118 //cmdreg_tmp = state_fio->FgetUint8();
2119 //trkreg = state_fio->FgetUint8();
2120 //secreg = state_fio->FgetUint8();
2121 //datareg = state_fio->FgetUint8();
2122 //drvreg = state_fio->FgetUint8();
2123 //sidereg = state_fio->FgetUint8();
2124 //cmdtype = state_fio->FgetUint8();
2125 //state_fio->Fread(register_id, sizeof(register_id), 1);
2126 //now_search = state_fio->FgetBool();
2127 //now_seek = state_fio->FgetBool();
2128 //sector_changed = state_fio->FgetBool();
2129 //no_command = state_fio->FgetInt32();
2130 //seektrk = state_fio->FgetInt32();
2131 //seekvct = state_fio->FgetBool();
2132 //motor_on = state_fio->FgetBool();
2133 //drive_sel = state_fio->FgetBool();
2134 //prev_drq_clock = state_fio->FgetUint32();
2135 //seekend_clock = state_fio->FgetUint32();
2137 fdc_debug_log = config.special_debug_fdc;