2 Skelton for retropc emulator
5 Author : Takeda.Toshiya
8 [ MB8877 / MB8876 / MB8866 ]
14 #define FDC_ST_BUSY 0x01 // busy
15 #define FDC_ST_INDEX 0x02 // index hole
16 #define FDC_ST_DRQ 0x02 // data request
17 #define FDC_ST_TRACK00 0x04 // track0
18 #define FDC_ST_LOSTDATA 0x04 // data lost
19 #define FDC_ST_CRCERR 0x08 // crc error
20 #define FDC_ST_SEEKERR 0x10 // seek error
21 #define FDC_ST_RECNFND 0x10 // sector not found
22 #define FDC_ST_HEADENG 0x20 // head engage
23 #define FDC_ST_RECTYPE 0x20 // record type
24 #define FDC_ST_WRITEFAULT 0x20 // write fault
25 #define FDC_ST_WRITEP 0x40 // write protectdc
26 #define FDC_ST_NOTREADY 0x80 // media not inserted
28 #define FDC_CMD_TYPE1 1
29 #define FDC_CMD_RD_SEC 2
30 #define FDC_CMD_RD_MSEC 3
31 #define FDC_CMD_WR_SEC 4
32 #define FDC_CMD_WR_MSEC 5
33 #define FDC_CMD_RD_ADDR 6
34 #define FDC_CMD_RD_TRK 7
35 #define FDC_CMD_WR_TRK 8
38 #define EVENT_SEEKEND 1
39 #define EVENT_SEARCH 2
41 #define EVENT_MULTI1 4
42 #define EVENT_MULTI2 5
45 #define DRIVE_MASK (MAX_DRIVE - 1)
47 #define DELAY_TIME (disk[drvreg]->drive_type == DRIVE_TYPE_2HD ? 15000 : 30000)
49 static const int seek_wait_hi[4] = {3000, 6000, 10000, 16000}; // 2MHz
50 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000}; // 1MHz
52 void MB8877::cancel_my_event(int event)
54 if(register_id[event] != -1) {
55 cancel_event(this, register_id[event]);
56 register_id[event] = -1;
60 void MB8877::register_my_event(int event, double usec)
62 cancel_my_event(event);
63 register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, ®ister_id[event]);
66 void MB8877::register_seek_event()
68 cancel_my_event(EVENT_SEEK);
69 if(fdc[drvreg].track == seektrk) {
70 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), 1, false, ®ister_id[EVENT_SEEK]);
71 } else if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
72 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
74 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
79 void MB8877::register_drq_event(int bytes)
81 double usec = disk[drvreg]->get_usec_per_bytes(bytes) - passed_usec(prev_drq_clock);
85 #if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
86 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER) {
89 #elif defined(_X1TURBO) || defined(_X1TURBOZ)
90 // if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
96 cancel_my_event(EVENT_DRQ);
97 register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, ®ister_id[EVENT_DRQ]);
100 void MB8877::register_lost_event(int bytes)
102 cancel_my_event(EVENT_LOST);
103 register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, ®ister_id[EVENT_LOST]);
106 void MB8877::initialize()
108 // initialize d88 handler
109 for(int i = 0; i < MAX_DRIVE; i++) {
110 disk[i] = new DISK(emu);
114 memset(fdc, 0, sizeof(fdc));
119 status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
121 prev_drq_clock = seekend_clock = 0;
124 void MB8877::release()
126 // release d88 handler
127 for(int i = 0; i < MAX_DRIVE; i++) {
137 for(int i = 0; i < MAX_DRIVE; i++) {
140 fdc[i].access = false;
142 for(int i = 0; i < array_length(register_id); i++) {
145 now_search = now_seek = drive_sel = false;
149 void MB8877::write_io8(uint32 addr, uint32 data)
155 #if defined(HAS_MB8866) || defined(HAS_MB8876)
156 cmdreg = (~data) & 0xff;
165 #if defined(HAS_MB8866) || defined(HAS_MB8876)
166 trkreg = (~data) & 0xff;
170 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
171 // track reg is written after command starts
172 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
179 #if defined(HAS_MB8866) || defined(HAS_MB8876)
180 secreg = (~data) & 0xff;
184 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
185 // sector reg is written after command starts
186 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
193 #if defined(HAS_MB8866) || defined(HAS_MB8876)
194 datareg = (~data) & 0xff;
198 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
199 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
200 // write or multisector write
201 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
202 if(!disk[drvreg]->write_protected) {
203 if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
204 disk[drvreg]->sector[fdc[drvreg].index] = datareg;
205 sector_changed = true;
208 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
210 status |= FDC_ST_WRITEFAULT;
211 status &= ~FDC_ST_BUSY;
215 //fdc[drvreg].index++;
217 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
218 if(cmdtype == FDC_CMD_WR_SEC) {
220 status &= ~FDC_ST_BUSY;
225 register_my_event(EVENT_MULTI1, 30);
226 register_my_event(EVENT_MULTI2, 60);
228 sector_changed = false;
229 } else if(status & FDC_ST_DRQ) {
230 if(fdc[drvreg].index == 0) {
231 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
233 register_drq_event(1);
236 status &= ~FDC_ST_DRQ;
237 } else if(cmdtype == FDC_CMD_WR_TRK) {
239 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
240 if(!disk[drvreg]->write_protected) {
241 if(fdc[drvreg].index == 0) {
242 disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
243 fdc[drvreg].id_written = false;
244 fdc[drvreg].side = sidereg;
245 fdc[drvreg].side_changed = false;
247 if(fdc[drvreg].side != sidereg) {
248 fdc[drvreg].side_changed = true;
250 if(fdc[drvreg].side_changed) {
251 // abort write track because disk side is changed
252 } else if(datareg == 0xf5) {
253 // write a1h in missing clock
254 } else if(datareg == 0xf6) {
255 // write c2h in missing clock
256 } else if(datareg == 0xf7) {
258 if(!fdc[drvreg].id_written) {
259 // insert new sector with data crc error
261 uint8 c = 0, h = 0, r = 0, n = 0;
262 fdc[drvreg].id_written = true;
263 fdc[drvreg].sector_found = false;
264 if(fdc[drvreg].index >= 4) {
265 c = disk[drvreg]->track[fdc[drvreg].index - 4];
266 h = disk[drvreg]->track[fdc[drvreg].index - 3];
267 r = disk[drvreg]->track[fdc[drvreg].index - 2];
268 n = disk[drvreg]->track[fdc[drvreg].index - 1];
270 fdc[drvreg].sector_length = 0x80 << (n & 3);
271 fdc[drvreg].sector_index = 0;
272 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
273 } else if(fdc[drvreg].sector_found) {
274 // clear data crc error if all sector data are written
275 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
276 disk[drvreg]->set_data_crc_error(false);
278 fdc[drvreg].id_written = false;
280 // data mark of current sector is not written
281 disk[drvreg]->set_data_mark_missing();
284 } else if(fdc[drvreg].id_written) {
285 if(fdc[drvreg].sector_found) {
287 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
288 disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
290 fdc[drvreg].sector_index++;
291 } else if(datareg == 0xf8 || datareg == 0xfb) {
293 disk[drvreg]->set_deleted(datareg == 0xf8);
294 fdc[drvreg].sector_found = true;
297 disk[drvreg]->track[fdc[drvreg].index] = datareg;
299 status |= FDC_ST_WRITEFAULT;
300 status &= ~FDC_ST_BUSY;
301 status &= ~FDC_ST_DRQ;
305 //fdc[drvreg].index++;
307 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
308 if(!disk[drvreg]->write_protected) {
309 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
310 // data mark of last sector is not written
311 disk[drvreg]->set_data_mark_missing();
313 disk[drvreg]->sync_buffer();
315 status &= ~FDC_ST_BUSY;
318 } else if(status & FDC_ST_DRQ) {
319 if(fdc[drvreg].index == 0) {
320 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
322 register_drq_event(1);
325 status &= ~FDC_ST_DRQ;
327 if(!(status & FDC_ST_DRQ)) {
328 cancel_my_event(EVENT_LOST);
330 fdc[drvreg].access = true;
337 uint32 MB8877::read_io8(uint32 addr)
348 // disk not inserted, motor stop
349 if(!disk[drvreg]->inserted || !motor_on) {
350 status |= FDC_ST_NOTREADY;
352 status &= ~FDC_ST_NOTREADY;
355 if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
356 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
357 status |= FDC_ST_WRITEP;
359 status &= ~FDC_ST_WRITEP;
362 status &= ~FDC_ST_WRITEP;
364 // track0, index hole
365 if(cmdtype == FDC_CMD_TYPE1) {
366 if(fdc[drvreg].track == 0) {
367 status |= FDC_ST_TRACK00;
369 status &= ~FDC_ST_TRACK00;
371 // index hole signal width is 5msec (thanks Mr.Sato)
372 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
373 status |= FDC_ST_INDEX;
375 status &= ~FDC_ST_INDEX;
378 // show busy a moment
380 if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
381 status &= ~FDC_ST_BUSY;
382 #ifdef MB8877_NO_BUSY_AFTER_SEEK
387 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
388 // MZ-2000 HuBASIC invites NOT READY status
389 if(++no_command == 16) {
390 val |= FDC_ST_NOTREADY;
396 if(!(status & FDC_ST_DRQ)) {
399 if(!(status & FDC_ST_BUSY)) {
402 #ifdef _FDC_DEBUG_LOG
403 emu->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
405 #if defined(HAS_MB8866) || defined(HAS_MB8876)
406 return (~val) & 0xff;
412 #if defined(HAS_MB8866) || defined(HAS_MB8876)
413 return (~trkreg) & 0xff;
419 #if defined(HAS_MB8866) || defined(HAS_MB8876)
420 return (~secreg) & 0xff;
426 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
427 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
428 // read or multisector read
429 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
430 datareg = disk[drvreg]->sector[fdc[drvreg].index];
431 //fdc[drvreg].index++;
433 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
435 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
437 #ifdef _FDC_DEBUG_LOG
438 emu->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
440 status |= FDC_ST_CRCERR;
441 status &= ~FDC_ST_BUSY;
444 } else if(cmdtype == FDC_CMD_RD_SEC) {
446 #ifdef _FDC_DEBUG_LOG
447 emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
449 status &= ~FDC_ST_BUSY;
454 #ifdef _FDC_DEBUG_LOG
455 emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
457 register_my_event(EVENT_MULTI1, 30);
458 register_my_event(EVENT_MULTI2, 60);
461 register_drq_event(1);
463 status &= ~FDC_ST_DRQ;
464 } else if(cmdtype == FDC_CMD_RD_ADDR) {
466 if(fdc[drvreg].index < 6) {
467 datareg = disk[drvreg]->id[fdc[drvreg].index];
468 //fdc[drvreg].index++;
470 if((fdc[drvreg].index + 1) >= 6) {
471 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
473 status |= FDC_ST_CRCERR;
475 status &= ~FDC_ST_BUSY;
478 #ifdef _FDC_DEBUG_LOG
479 emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
482 register_drq_event(1);
484 status &= ~FDC_ST_DRQ;
485 } else if(cmdtype == FDC_CMD_RD_TRK) {
487 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
488 datareg = disk[drvreg]->track[fdc[drvreg].index];
489 //fdc[drvreg].index++;
491 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
492 #ifdef _FDC_DEBUG_LOG
493 emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));
495 status &= ~FDC_ST_BUSY;
496 status |= FDC_ST_LOSTDATA;
500 register_drq_event(1);
502 status &= ~FDC_ST_DRQ;
504 if(!(status & FDC_ST_DRQ)) {
505 cancel_my_event(EVENT_LOST);
507 fdc[drvreg].access = true;
510 #ifdef _FDC_DEBUG_LOG
511 emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
513 #if defined(HAS_MB8866) || defined(HAS_MB8876)
514 return (~datareg) & 0xff;
522 void MB8877::write_dma_io8(uint32 addr, uint32 data)
527 uint32 MB8877::read_dma_io8(uint32 addr)
532 void MB8877::write_signal(int id, uint32 data, uint32 mask)
534 if(id == SIG_MB8877_DRIVEREG) {
535 drvreg = data & DRIVE_MASK;
537 seekend_clock = current_clock();
538 } else if(id == SIG_MB8877_SIDEREG) {
539 sidereg = (data & mask) ? 1 : 0;
540 } else if(id == SIG_MB8877_MOTOR) {
541 motor_on = ((data & mask) != 0);
545 uint32 MB8877::read_signal(int ch)
549 for(int i = 0; i < MAX_DRIVE; i++) {
553 fdc[i].access = false;
561 void MB8877::event_callback(int event_id, int err)
563 int event = event_id >> 8;
564 int cmd = event_id & 0xff;
565 register_id[event] = -1;
567 // cancel event if the command is finished or other command is executed
569 if(event == EVENT_SEEK || event == EVENT_SEEKEND) {
571 } else if(event == EVENT_SEARCH) {
579 #ifdef _FDC_DEBUG_LOG
580 //emu->out_debug_log(_T("FDC\tSEEK START\n"));
582 if(seektrk > fdc[drvreg].track) {
584 } else if(seektrk < fdc[drvreg].track) {
587 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
588 trkreg = fdc[drvreg].track;
590 //emu->out_debug_log(_T("Track %d\n"), trkreg);
591 if(seektrk != fdc[drvreg].track) {
592 register_seek_event();
595 seekend_clock = current_clock();
599 status_tmp |= search_track();
601 if(status_tmp & FDC_ST_SEEKERR) {
602 time = get_usec_to_detect_index_hole(5, true);
604 time = get_usec_to_next_trans_pos(true);
606 register_my_event(EVENT_SEEKEND, time);
613 #ifdef _FDC_DEBUG_LOG
614 //emu->out_debug_log(_T("FDC\tSEEK END\n"));
619 if(status_tmp & FDC_ST_RECNFND) {
620 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
621 // for SHARP X1 Batten Tanuki
622 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
623 status_tmp &= ~FDC_ST_RECNFND;
626 #ifdef _FDC_DEBUG_LOG
627 emu->out_debug_log(_T("FDC\tSEARCH NG\n"));
629 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
632 } else if(status_tmp & FDC_ST_WRITEFAULT) {
633 #ifdef _FDC_DEBUG_LOG
634 emu->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
636 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
640 status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
641 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
642 register_lost_event(8);
643 } else if(cmdtype == FDC_CMD_WR_TRK) {
644 register_lost_event(3);
646 register_lost_event(1);
648 fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
649 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
652 #ifdef _FDC_DEBUG_LOG
653 emu->out_debug_log(_T("FDC\tSEARCH OK\n"));
658 if(status & FDC_ST_BUSY) {
659 status |= FDC_ST_DRQ;
660 register_lost_event(1);
661 if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
662 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
664 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
666 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
667 cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
668 cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK ||
669 cmdtype == FDC_CMD_RD_ADDR) {
672 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
674 #ifdef _FDC_DEBUG_LOG
675 //emu->out_debug_log(_T("FDC\tDRQ!\n"));
683 if(cmdtype == FDC_CMD_RD_MSEC) {
685 } else if(cmdtype == FDC_CMD_WR_MSEC) {
686 cmd_writedata(false);
690 if(status & FDC_ST_BUSY) {
691 #ifdef _FDC_DEBUG_LOG
692 emu->out_debug_log("FDC\tDATA LOST\n");
694 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
695 if(fdc[drvreg].index == 0) {
696 status &= ~FDC_ST_BUSY;
697 //status &= ~FDC_ST_DRQ;
707 status |= FDC_ST_LOSTDATA;
713 // ----------------------------------------------------------------------------
715 // ----------------------------------------------------------------------------
717 void MB8877::process_cmd()
719 #ifdef _FDC_DEBUG_LOG
720 static const _TCHAR *cmdstr[0x10] = {
721 _T("RESTORE "), _T("SEEK "), _T("STEP "), _T("STEP "),
722 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
723 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
724 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
726 emu->out_debug_log(_T("FDC\tCMD=%2xh (%s) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, cmdstr[cmdreg >> 4], datareg, drvreg, trkreg, sidereg, secreg);
730 switch(cmdreg & 0xf0) {
778 void MB8877::cmd_restore()
781 cmdtype = FDC_CMD_TYPE1;
782 status = FDC_ST_HEADENG | FDC_ST_BUSY;
789 register_seek_event();
792 void MB8877::cmd_seek()
795 cmdtype = FDC_CMD_TYPE1;
796 status = FDC_ST_HEADENG | FDC_ST_BUSY;
798 // seektrk = (uint8)(fdc[drvreg].track + datareg - trkreg);
800 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
801 seekvct = !(datareg > trkreg);
803 register_seek_event();
806 void MB8877::cmd_step()
816 void MB8877::cmd_stepin()
819 cmdtype = FDC_CMD_TYPE1;
820 status = FDC_ST_HEADENG | FDC_ST_BUSY;
822 seektrk = fdc[drvreg].track + 1;
823 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
826 register_seek_event();
829 void MB8877::cmd_stepout()
832 cmdtype = FDC_CMD_TYPE1;
833 status = FDC_ST_HEADENG | FDC_ST_BUSY;
835 seektrk = fdc[drvreg].track - 1;
836 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
839 register_seek_event();
842 void MB8877::cmd_readdata(bool first_sector)
845 cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
846 status = FDC_ST_BUSY;
847 status_tmp = search_sector();
851 if(status_tmp & FDC_ST_RECNFND) {
852 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
854 time = get_usec_to_start_trans(first_sector);
856 register_my_event(EVENT_SEARCH, time);
857 cancel_my_event(EVENT_LOST);
860 void MB8877::cmd_writedata(bool first_sector)
863 cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
864 status = FDC_ST_BUSY;
865 status_tmp = search_sector() & ~FDC_ST_RECTYPE;
867 sector_changed = false;
870 if(status_tmp & FDC_ST_RECNFND) {
871 time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
872 } else if(status & FDC_ST_WRITEFAULT) {
873 time = (cmdreg & 4) ? DELAY_TIME : 1;
875 time = get_usec_to_start_trans(first_sector);
877 register_my_event(EVENT_SEARCH, time);
878 cancel_my_event(EVENT_LOST);
881 void MB8877::cmd_readaddr()
883 // type-3 read address
884 cmdtype = FDC_CMD_RD_ADDR;
885 status = FDC_ST_BUSY;
886 status_tmp = search_addr();
890 if(status_tmp & FDC_ST_RECNFND) {
891 time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
893 time = get_usec_to_start_trans(true);
895 register_my_event(EVENT_SEARCH, time);
896 cancel_my_event(EVENT_LOST);
899 void MB8877::cmd_readtrack()
902 cmdtype = FDC_CMD_RD_TRK;
903 status = FDC_ST_BUSY;
906 disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
907 fdc[drvreg].index = 0;
910 fdc[drvreg].next_trans_position = 1;
911 double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
912 register_my_event(EVENT_SEARCH, time);
913 cancel_my_event(EVENT_LOST);
916 void MB8877::cmd_writetrack()
918 // type-3 write track
919 cmdtype = FDC_CMD_WR_TRK;
920 status = FDC_ST_BUSY;
923 fdc[drvreg].index = 0;
924 fdc[drvreg].id_written = false;
928 if(disk[drvreg]->write_protected) {
929 status_tmp = FDC_ST_WRITEFAULT;
930 time = (cmdreg & 4) ? DELAY_TIME : 1;
933 // wait 15msec before raise first drq
934 fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
937 // raise first drq soon
938 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
939 time = disk[drvreg]->get_usec_per_bytes(1);
941 // wait at least 3bytes before check index hole and raise second drq
942 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
943 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
944 fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
947 register_my_event(EVENT_SEARCH, time);
948 cancel_my_event(EVENT_LOST);
951 void MB8877::cmd_forceint()
953 // type-4 force interrupt
954 if(cmdtype == FDC_CMD_TYPE1) {
955 // abort restore/seek/step command
957 if(seektrk > fdc[drvreg].track) {
959 } else if(seektrk < fdc[drvreg].track) {
962 if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
963 trkreg = fdc[drvreg].track;
966 } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
967 // abort write sector command
969 disk[drvreg]->set_data_crc_error(false);
971 } else if(cmdtype == FDC_CMD_WR_TRK) {
972 // abort write track command
973 if(!disk[drvreg]->write_protected) {
974 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
975 // data mark of last sector is not written
976 disk[drvreg]->set_data_mark_missing();
978 disk[drvreg]->sync_buffer();
981 now_search = now_seek = sector_changed = false;
983 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
984 cmdtype = FDC_CMD_TYPE1;
985 status = FDC_ST_HEADENG;
987 status &= ~FDC_ST_BUSY;
989 // force interrupt if bit0-bit3 is high
994 cancel_my_event(EVENT_SEEK);
995 cancel_my_event(EVENT_SEEKEND);
996 cancel_my_event(EVENT_SEARCH);
997 cancel_my_event(EVENT_DRQ);
998 cancel_my_event(EVENT_MULTI1);
999 cancel_my_event(EVENT_MULTI2);
1000 cancel_my_event(EVENT_LOST);
1003 // ----------------------------------------------------------------------------
1005 // ----------------------------------------------------------------------------
1007 uint8 MB8877::search_track()
1010 int track = fdc[drvreg].track;
1011 if(!disk[drvreg]->get_track(track, sidereg)){
1012 return FDC_ST_SEEKERR;
1015 // verify track number
1016 if(disk[drvreg]->ignore_crc()) {
1017 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1018 disk[drvreg]->get_sector(-1, -1, i);
1019 if(disk[drvreg]->id[0] == trkreg) {
1020 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1021 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1026 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1027 disk[drvreg]->get_sector(-1, -1, i);
1028 if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1029 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1030 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1034 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1035 disk[drvreg]->get_sector(-1, -1, i);
1036 if(disk[drvreg]->id[0] == trkreg) {
1037 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1041 return FDC_ST_SEEKERR;
1044 uint8 MB8877::search_sector()
1047 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1048 if(disk[drvreg]->write_protected) {
1049 return FDC_ST_WRITEFAULT;
1054 int track = fdc[drvreg].track;
1055 #if defined(_FM77AV_VARIANTS)
1056 if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
1057 if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
1058 (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
1059 (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
1064 if(!disk[drvreg]->get_track(track, sidereg)) {
1065 return FDC_ST_RECNFND;
1068 // get current position
1069 int sector_num = disk[drvreg]->sector_num.sd;
1070 int position = get_cur_position();
1072 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1073 position -= disk[drvreg]->get_track_size();
1076 // first scanned sector
1077 int first_sector = 0;
1078 for(int i = 0; i < sector_num; i++) {
1079 if(position < disk[drvreg]->am1_position[i]) {
1086 for(int i = 0; i < sector_num; i++) {
1088 int index = (first_sector + i) % sector_num;
1089 disk[drvreg]->get_sector(-1, -1, index);
1092 if(disk[drvreg]->id[0] != trkreg) {
1095 #if !defined(HAS_MB8866)
1096 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1100 if(disk[drvreg]->id[2] != secreg) {
1103 if(disk[drvreg]->sector_size.sd == 0) {
1106 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1108 disk[drvreg]->sector_size.sd = 0;
1109 return FDC_ST_RECNFND | FDC_ST_CRCERR;
1113 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1114 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1115 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[i] - fdc[drvreg].next_trans_position;
1117 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i] + 1;
1119 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
1120 fdc[drvreg].index = 0;
1121 #ifdef _FDC_DEBUG_LOG
1122 emu->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1123 disk[drvreg]->sector_size.sd,
1124 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1125 disk[drvreg]->id[4], disk[drvreg]->id[5],
1126 disk[drvreg]->data_crc_error ? 1 : 0);
1128 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1132 disk[drvreg]->sector_size.sd = 0;
1133 return FDC_ST_RECNFND;
1136 uint8 MB8877::search_addr()
1139 int track = fdc[drvreg].track;
1140 #if defined(_FM77AV_VARIANTS)
1141 if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
1142 if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
1143 (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
1144 (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
1149 if(!disk[drvreg]->get_track(track, sidereg)) {
1150 return FDC_ST_RECNFND;
1153 // get current position
1154 int sector_num = disk[drvreg]->sector_num.sd;
1155 int position = get_cur_position();
1157 if(position > disk[drvreg]->am1_position[sector_num - 1]) {
1158 position -= disk[drvreg]->get_track_size();
1161 // first scanned sector
1162 int first_sector = 0;
1163 for(int i = 0; i < sector_num; i++) {
1164 if(position < disk[drvreg]->am1_position[i]) {
1171 if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1172 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
1173 fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
1174 fdc[drvreg].index = 0;
1175 secreg = disk[drvreg]->id[0];
1180 disk[drvreg]->sector_size.sd = 0;
1181 return FDC_ST_RECNFND;
1184 // ----------------------------------------------------------------------------
1186 // ----------------------------------------------------------------------------
1188 int MB8877::get_cur_position()
1190 return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1193 double MB8877::get_usec_to_start_trans(bool first_sector)
1195 #if defined(_X1TURBO) || defined(_X1TURBOZ)
1196 // FIXME: ugly patch for X1turbo ALPHA
1197 // if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
1201 if(/*disk[drvreg]->no_skew &&*/ !disk[drvreg]->correct_timing()) {
1202 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1203 // so use the constant period to search the target sector
1207 // get time from current position
1208 double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
1209 if(first_sector && time < 60000 - passed_usec(seekend_clock)) {
1210 time += disk[drvreg]->get_usec_per_track();
1215 double MB8877::get_usec_to_next_trans_pos(bool delay)
1217 int position = get_cur_position();
1219 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1221 int bytes = fdc[drvreg].next_trans_position - position;
1223 if(fdc[drvreg].next_am1_position < position || bytes < 0) {
1224 bytes += disk[drvreg]->get_track_size();
1227 // bytes += disk[drvreg]->get_track_size();
1229 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1236 double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
1238 int position = get_cur_position();
1240 position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
1242 int bytes = disk[drvreg]->get_track_size() * count - position;
1244 bytes += disk[drvreg]->get_track_size();
1246 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1253 // ----------------------------------------------------------------------------
1255 // ----------------------------------------------------------------------------
1257 void MB8877::set_irq(bool val)
1259 write_signals(&outputs_irq, val ? 0xffffffff : 0);
1262 void MB8877::set_drq(bool val)
1264 write_signals(&outputs_drq, val ? 0xffffffff : 0);
1267 // ----------------------------------------------------------------------------
1269 // ----------------------------------------------------------------------------
1271 void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
1273 if(drv < MAX_DRIVE) {
1274 disk[drv]->open(file_path, bank);
1278 void MB8877::close_disk(int drv)
1280 if(drv < MAX_DRIVE) {
1286 bool MB8877::disk_inserted(int drv)
1288 if(drv < MAX_DRIVE) {
1289 return disk[drv]->inserted;
1294 void MB8877::set_disk_protected(int drv, bool value)
1296 if(drv < MAX_DRIVE) {
1297 disk[drv]->write_protected = value;
1301 bool MB8877::get_disk_protected(int drv)
1303 if(drv < MAX_DRIVE) {
1304 return disk[drv]->write_protected;
1309 void MB8877::set_drive_type(int drv, uint8 type)
1311 if(drv < MAX_DRIVE) {
1312 disk[drv]->drive_type = type;
1316 uint8 MB8877::get_drive_type(int drv)
1318 if(drv < MAX_DRIVE) {
1319 return disk[drv]->drive_type;
1321 return DRIVE_TYPE_UNK;
1324 void MB8877::set_drive_rpm(int drv, int rpm)
1326 if(drv < MAX_DRIVE) {
1327 disk[drv]->drive_rpm = rpm;
1331 void MB8877::set_drive_mfm(int drv, bool mfm)
1333 if(drv < MAX_DRIVE) {
1334 disk[drv]->drive_mfm = mfm;
1338 uint8 MB8877::fdc_status()
1340 // for each virtual machines
1341 #if defined(_FMR50) || defined(_FMR60)
1342 return disk[drvreg]->inserted ? 2 : 0;
1348 #define STATE_VERSION 5
1350 void MB8877::save_state(FILEIO* state_fio)
1352 state_fio->FputUint32(STATE_VERSION);
1353 state_fio->FputInt32(this_device_id);
1355 state_fio->Fwrite(fdc, sizeof(fdc), 1);
1356 for(int i = 0; i < MAX_DRIVE; i++) {
1357 disk[i]->save_state(state_fio);
1359 state_fio->FputUint8(status);
1360 state_fio->FputUint8(status_tmp);
1361 state_fio->FputUint8(cmdreg);
1362 state_fio->FputUint8(cmdreg_tmp);
1363 state_fio->FputUint8(trkreg);
1364 state_fio->FputUint8(secreg);
1365 state_fio->FputUint8(datareg);
1366 state_fio->FputUint8(drvreg);
1367 state_fio->FputUint8(sidereg);
1368 state_fio->FputUint8(cmdtype);
1369 state_fio->Fwrite(register_id, sizeof(register_id), 1);
1370 state_fio->FputBool(now_search);
1371 state_fio->FputBool(now_seek);
1372 state_fio->FputBool(sector_changed);
1373 state_fio->FputInt32(no_command);
1374 state_fio->FputInt32(seektrk);
1375 state_fio->FputBool(seekvct);
1376 state_fio->FputBool(motor_on);
1377 state_fio->FputBool(drive_sel);
1378 state_fio->FputUint32(prev_drq_clock);
1379 state_fio->FputUint32(seekend_clock);
1382 bool MB8877::load_state(FILEIO* state_fio)
1384 if(state_fio->FgetUint32() != STATE_VERSION) {
1387 if(state_fio->FgetInt32() != this_device_id) {
1390 state_fio->Fread(fdc, sizeof(fdc), 1);
1391 for(int i = 0; i < MAX_DRIVE; i++) {
1392 if(!disk[i]->load_state(state_fio)) {
1396 status = state_fio->FgetUint8();
1397 status_tmp = state_fio->FgetUint8();
1398 cmdreg = state_fio->FgetUint8();
1399 cmdreg_tmp = state_fio->FgetUint8();
1400 trkreg = state_fio->FgetUint8();
1401 secreg = state_fio->FgetUint8();
1402 datareg = state_fio->FgetUint8();
1403 drvreg = state_fio->FgetUint8();
1404 sidereg = state_fio->FgetUint8();
1405 cmdtype = state_fio->FgetUint8();
1406 state_fio->Fread(register_id, sizeof(register_id), 1);
1407 now_search = state_fio->FgetBool();
1408 now_seek = state_fio->FgetBool();
1409 sector_changed = state_fio->FgetBool();
1410 no_command = state_fio->FgetInt32();
1411 seektrk = state_fio->FgetInt32();
1412 seekvct = state_fio->FgetBool();
1413 motor_on = state_fio->FgetBool();
1414 drive_sel = state_fio->FgetBool();
1415 prev_drq_clock = state_fio->FgetUint32();
1416 seekend_clock = state_fio->FgetUint32();