2 Skelton for retropc emulator
5 Author : Takeda.Toshiya
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 static const int seek_wait_hi[4] = {3000, 6000, 10000, 16000};
48 static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000};
51 #define CANCEL_EVENT(event) { \
52 if(register_id[event] != -1) { \
53 cancel_event(this, register_id[event]); \
54 register_id[event] = -1; \
57 #define REGISTER_EVENT(event, usec) { \
58 if(register_id[event] != -1) { \
59 cancel_event(this, register_id[event]); \
60 register_id[event] = -1; \
62 register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, ®ister_id[event]); \
64 #define REGISTER_SEEK_EVENT() { \
65 if(register_id[EVENT_SEEK] != -1) { \
66 cancel_event(this, register_id[EVENT_SEEK]); \
67 register_id[EVENT_SEEK] = -1; \
69 if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) { \
70 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, ®ister_id[EVENT_SEEK]); \
72 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, ®ister_id[EVENT_SEEK]); \
74 now_seek = after_seek = true; \
77 #define REGISTER_DRQ_EVENT() { \
78 double usec = disk[drvreg]->get_usec_per_bytes(1) - passed_usec(prev_drq_clock); \
82 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_ALPHA) { \
86 } else if(disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER) { \
89 if(register_id[EVENT_DRQ] != -1) { \
90 cancel_event(this, register_id[EVENT_DRQ]); \
91 register_id[EVENT_DRQ] = -1; \
93 register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, ®ister_id[EVENT_DRQ]); \
96 #define REGISTER_LOST_EVENT() { \
97 if(register_id[EVENT_LOST] != -1) { \
98 cancel_event(this, register_id[EVENT_LOST]); \
99 register_id[EVENT_LOST] = -1; \
101 register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(/*1*/2), false, ®ister_id[EVENT_LOST]); \
105 void MB8877::cancel_my_event(int event)
107 if(register_id[event] != -1) {
108 cancel_event(this, register_id[event]);
109 register_id[event] = -1;
113 void MB8877::register_my_event(int event, double usec)
115 cancel_my_event(event);
116 register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, ®ister_id[event]);
119 void MB8877::register_seek_event()
121 cancel_my_event(EVENT_SEEK);
122 if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
123 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
125 register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, ®ister_id[EVENT_SEEK]);
130 void MB8877::register_drq_event(int bytes)
132 double usec = disk[drvreg]->get_usec_per_bytes(bytes) - passed_usec(prev_drq_clock);
136 #if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
137 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER) {
140 #elif defined(_X1TURBO) || defined(_X1TURBOZ)
141 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
147 cancel_my_event(EVENT_DRQ);
148 register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, ®ister_id[EVENT_DRQ]);
151 void MB8877::register_lost_event(int bytes)
153 cancel_my_event(EVENT_LOST);
154 register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, ®ister_id[EVENT_LOST]);
157 void MB8877::initialize()
159 // initialize d88 handler
160 for(int i = 0; i < MAX_DRIVE; i++) {
161 disk[i] = new DISK(emu);
165 memset(fdc, 0, sizeof(fdc));
170 status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
172 prev_drq_clock = seekend_clock = 0;
175 void MB8877::release()
177 // release d88 handler
178 for(int i = 0; i < MAX_DRIVE; i++) {
188 for(int i = 0; i < MAX_DRIVE; i++) {
191 fdc[i].access = false;
193 for(int i = 0; i < array_length(register_id); i++) {
196 now_search = now_seek = drive_sel = false;
200 void MB8877::write_io8(uint32 addr, uint32 data)
207 cmdreg = (~data) & 0xff;
217 trkreg = (~data) & 0xff;
221 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
222 // track reg is written after command starts
223 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
231 secreg = (~data) & 0xff;
235 if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
236 // sector reg is written after command starts
237 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
245 datareg = (~data) & 0xff;
249 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
250 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
251 // write or multisector write
252 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
253 if(!disk[drvreg]->write_protected) {
254 disk[drvreg]->sector[fdc[drvreg].index] = datareg;
256 disk[drvreg]->set_deleted((cmdreg & 1) != 0);
258 status |= FDC_ST_WRITEFAULT;
259 status &= ~FDC_ST_BUSY;
263 //fdc[drvreg].index++;
265 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
266 if(cmdtype == FDC_CMD_WR_SEC) {
268 status &= ~FDC_ST_BUSY;
273 register_my_event(EVENT_MULTI1, 30);
274 register_my_event(EVENT_MULTI2, 60);
276 } else if(status & FDC_ST_DRQ) {
277 if(fdc[drvreg].index == 0) {
278 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
280 register_drq_event(1);
283 status &= ~FDC_ST_DRQ;
284 } else if(cmdtype == FDC_CMD_WR_TRK) {
286 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
287 if(!disk[drvreg]->write_protected) {
288 if(fdc[drvreg].index == 0) {
289 disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
290 fdc[drvreg].id_written = false;
291 fdc[drvreg].side = sidereg;
292 fdc[drvreg].side_changed = false;
294 if(fdc[drvreg].side != sidereg) {
295 fdc[drvreg].side_changed = true;
297 if(fdc[drvreg].side_changed) {
298 // abort write track because disk side is changed
299 } else if(datareg == 0xf5) {
300 // write a1h in missing clock
301 } else if(datareg == 0xf6) {
302 // write c2h in missing clock
303 } else if(datareg == 0xf7) {
305 if(!fdc[drvreg].id_written) {
306 // insert new sector with data crc error
308 uint8 c = 0, h = 0, r = 0, n = 0;
309 fdc[drvreg].id_written = true;
310 fdc[drvreg].sector_found = false;
311 if (fdc[drvreg].index >= 4) {
312 c = disk[drvreg]->track[fdc[drvreg].index - 4];
313 h = disk[drvreg]->track[fdc[drvreg].index - 3];
314 r = disk[drvreg]->track[fdc[drvreg].index - 2];
315 n = disk[drvreg]->track[fdc[drvreg].index - 1];
317 fdc[drvreg].sector_length = 0x80 << (n & 3);
318 fdc[drvreg].sector_index = 0;
319 disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
320 } else if(fdc[drvreg].sector_found) {
321 // clear data crc error if all sector data are written
322 if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
323 disk[drvreg]->clear_data_crc_error();
325 fdc[drvreg].id_written = false;
327 // data mark of current sector is not written
328 disk[drvreg]->set_data_mark_missing();
331 } else if(fdc[drvreg].id_written) {
332 if(fdc[drvreg].sector_found) {
334 if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
335 disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
337 fdc[drvreg].sector_index++;
338 } else if(datareg == 0xf8 || datareg == 0xfb) {
340 disk[drvreg]->set_deleted(datareg == 0xf8);
341 fdc[drvreg].sector_found = true;
344 disk[drvreg]->track[fdc[drvreg].index] = datareg;
346 status |= FDC_ST_WRITEFAULT;
347 status &= ~FDC_ST_BUSY;
348 status &= ~FDC_ST_DRQ;
352 //fdc[drvreg].index++;
354 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
355 if(!disk[drvreg]->write_protected) {
356 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
357 // data mark of last sector is not written
358 disk[drvreg]->set_data_mark_missing();
360 disk[drvreg]->sync_buffer();
362 status &= ~FDC_ST_BUSY;
365 } else if(status & FDC_ST_DRQ) {
366 if(fdc[drvreg].index == 0) {
367 register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
369 register_drq_event(1);
372 status &= ~FDC_ST_DRQ;
374 if(!(status & FDC_ST_DRQ)) {
375 cancel_my_event(EVENT_LOST);
377 fdc[drvreg].access = true;
384 uint32 MB8877::read_io8(uint32 addr)
395 // disk not inserted, motor stop
396 if(!disk[drvreg]->inserted || !motor_on) {
397 status |= FDC_ST_NOTREADY;
399 status &= ~FDC_ST_NOTREADY;
402 if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
403 if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
404 status |= FDC_ST_WRITEP;
406 status &= ~FDC_ST_WRITEP;
409 status &= ~FDC_ST_WRITEP;
411 // track0, index hole
412 if(cmdtype == FDC_CMD_TYPE1) {
413 if(fdc[drvreg].track == 0) {
414 status |= FDC_ST_TRACK00;
416 status &= ~FDC_ST_TRACK00;
418 if(!(status & FDC_ST_NOTREADY)) {
419 // index hole signal width is 5msec (thanks Mr.Sato)
420 if(get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
421 status |= FDC_ST_INDEX;
423 status &= ~FDC_ST_INDEX;
427 // show busy a moment
429 if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
430 status &= ~FDC_ST_BUSY;
433 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
434 // MZ-2000 HuBASIC invites NOT READY status
435 if(++no_command == 16) {
436 val |= FDC_ST_NOTREADY;
442 if(!(status & FDC_ST_DRQ)) {
445 if(!(status & FDC_ST_BUSY)) {
448 #ifdef _FDC_DEBUG_LOG
449 //emu->out_debug_log(_T("FDCSTATUS=%2x\n"), val);
452 return (~val) & 0xff;
459 return (~trkreg) & 0xff;
466 return (~secreg) & 0xff;
472 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
473 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
474 // read or multisector read
475 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
476 datareg = disk[drvreg]->sector[fdc[drvreg].index];
477 //fdc[drvreg].index++;
479 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
481 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
483 #ifdef _FDC_DEBUG_LOG
484 emu->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
486 status |= FDC_ST_CRCERR;
487 status &= ~FDC_ST_BUSY;
490 } else if(cmdtype == FDC_CMD_RD_SEC) {
492 #ifdef _FDC_DEBUG_LOG
493 emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
495 status &= ~FDC_ST_BUSY;
500 #ifdef _FDC_DEBUG_LOG
501 emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
503 register_my_event(EVENT_MULTI1, 30);
504 register_my_event(EVENT_MULTI2, 60);
507 register_drq_event(1);
509 status &= ~FDC_ST_DRQ;
510 } else if(cmdtype == FDC_CMD_RD_ADDR) {
512 if(fdc[drvreg].index < 6) {
513 datareg = disk[drvreg]->id[fdc[drvreg].index];
514 //fdc[drvreg].index++;
516 if((fdc[drvreg].index + 1) >= 6) {
517 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
519 status |= FDC_ST_CRCERR;
521 status &= ~FDC_ST_BUSY;
524 #ifdef _FDC_DEBUG_LOG
525 emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
528 register_drq_event(1);
530 status &= ~FDC_ST_DRQ;
531 } else if(cmdtype == FDC_CMD_RD_TRK) {
533 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
534 datareg = disk[drvreg]->track[fdc[drvreg].index];
535 //fdc[drvreg].index++;
537 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
538 #ifdef _FDC_DEBUG_LOG
539 emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));
541 status &= ~FDC_ST_BUSY;
542 status |= FDC_ST_LOSTDATA;
545 #ifdef _FDC_DEBUG_LOG
546 emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
549 register_drq_event(1);
551 status &= ~FDC_ST_DRQ;
553 if(!(status & FDC_ST_DRQ)) {
554 cancel_my_event(EVENT_LOST);
556 fdc[drvreg].access = true;
559 #ifdef _FDC_DEBUG_LOG
560 //emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
563 return (~datareg) & 0xff;
571 void MB8877::write_dma_io8(uint32 addr, uint32 data)
576 uint32 MB8877::read_dma_io8(uint32 addr)
581 void MB8877::write_signal(int id, uint32 data, uint32 mask)
583 if(id == SIG_MB8877_DRIVEREG) {
584 drvreg = data & DRIVE_MASK;
586 seekend_clock = current_clock();
587 } else if(id == SIG_MB8877_SIDEREG) {
588 sidereg = (data & mask) ? 1 : 0;
589 } else if(id == SIG_MB8877_MOTOR) {
590 motor_on = ((data & mask) != 0);
594 uint32 MB8877::read_signal(int ch)
599 for(int i = 0; i < MAX_DRIVE; i++) {
603 fdc[i].access = false;
611 void MB8877::event_callback(int event_id, int err)
613 int event = event_id >> 8;
614 int cmd = event_id & 0xff;
615 register_id[event] = -1;
617 // cancel event if the command is finished or other command is executed
619 if(event == EVENT_SEEK) {
621 } else if(event == EVENT_SEARCH) {
629 #ifdef _FDC_DEBUG_LOG
630 //emu->out_debug_log(_T("FDC\tSEEK START\n"));
632 if(seektrk > fdc[drvreg].track) {
634 } else if(seektrk < fdc[drvreg].track) {
637 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
638 trkreg = fdc[drvreg].track;
640 if(seektrk == fdc[drvreg].track) {
642 //if((cmdreg & 0xf0) == 0) {
646 status |= search_track();
649 seekend_clock = current_clock();
651 #ifdef _FDC_DEBUG_LOG
652 emu->out_debug_log(_T("FDC\tSEEKn"));
655 register_seek_event();
659 if(seektrk == fdc[drvreg].track) {
661 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
662 trkreg = fdc[drvreg].track;
664 //if((cmdreg & 0xf0) == 0) {
668 status |= search_track();
671 seekend_clock = current_clock();
672 // cancel_my_event(EVENT_SEEK);
674 #ifdef _FDC_DEBUG_LOG
675 emu->out_debug_log(_T("FDC\tSEEK END\n"));
681 if(!(status_tmp & FDC_ST_RECNFND)) {
682 status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
683 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
684 register_lost_event(8);
685 } else if(cmdtype == FDC_CMD_WR_TRK) {
686 register_lost_event(3);
688 // register_lost_event(1);
689 register_lost_event(2);
691 fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
692 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
695 #ifdef _FDC_DEBUG_LOG
696 emu->out_debug_log(_T("FDC\tSEARCH OK\n"));
699 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
700 // for SHARP X1 Batten Tanuki
701 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
702 status_tmp &= ~FDC_ST_RECNFND;
705 #ifdef _FDC_DEBUG_LOG
706 emu->out_debug_log(_T("FDC\tSEARCH NG\n"));
708 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
712 if(status & FDC_ST_BUSY) {
713 status |= FDC_ST_DRQ;
714 // register_lost_event(1);
715 register_lost_event(2);
716 if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
717 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
719 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
721 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
722 cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
723 cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK ||
724 cmdtype == FDC_CMD_RD_ADDR) {
727 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
729 #ifdef _FDC_DEBUG_LOG
730 //emu->out_debug_log(_T("FDC\tDRQ!\n"));
738 if(cmdtype == FDC_CMD_RD_MSEC) {
740 } else if(cmdtype == FDC_CMD_WR_MSEC) {
741 cmd_writedata(false);
745 if(status & FDC_ST_BUSY) {
746 #ifdef _FDC_DEBUG_LOG
747 emu->out_debug_log("FDC\tDATA LOST\n");
749 status |= FDC_ST_LOSTDATA;
750 status &= ~FDC_ST_BUSY;
751 //status &= ~FDC_ST_DRQ;
759 // ----------------------------------------------------------------------------
761 // ----------------------------------------------------------------------------
763 void MB8877::process_cmd()
765 #ifdef _FDC_DEBUG_LOG
766 static const _TCHAR *cmdstr[0x10] = {
767 _T("RESTORE "), _T("SEEK "), _T("STEP "), _T("STEP "),
768 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
769 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
770 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
772 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);
776 switch(cmdreg & 0xf0) {
824 void MB8877::cmd_restore()
827 cmdtype = FDC_CMD_TYPE1;
828 status = FDC_ST_HEADENG | FDC_ST_BUSY;
833 if(fdc[drvreg].track != seektrk) {
834 cancel_my_event(EVENT_SEEKEND);
835 register_seek_event();
837 cancel_my_event(EVENT_SEEK);
838 // register_my_event(EVENT_SEEKEND, 300);
839 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
842 #ifdef _FDC_DEBUG_LOG
843 emu->out_debug_log(_T("FDC\tSEEKn"));
847 void MB8877::cmd_seek()
850 cmdtype = FDC_CMD_TYPE1;
851 status = FDC_ST_HEADENG | FDC_ST_BUSY;
854 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
855 seekvct = !(datareg > trkreg);
857 if(fdc[drvreg].track != seektrk) {
858 cancel_my_event(EVENT_SEEKEND);
859 register_seek_event();
861 cancel_my_event(EVENT_SEEK);
862 // register_my_event(EVENT_SEEKEND, 300);
863 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
865 #ifdef _FDC_DEBUG_LOG
866 emu->out_debug_log(_T("FDC\tSEEKn"));
870 void MB8877::cmd_step()
880 void MB8877::cmd_stepin()
883 cmdtype = FDC_CMD_TYPE1;
884 status = FDC_ST_HEADENG | FDC_ST_BUSY;
886 seektrk = (fdc[drvreg].track < 83) ? fdc[drvreg].track + 1 : 83;
889 if(fdc[drvreg].track != seektrk) {
890 cancel_my_event(EVENT_SEEKEND);
891 register_seek_event();
893 cancel_my_event(EVENT_SEEK);
894 // register_my_event(EVENT_SEEKEND, 300);
895 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
897 #ifdef _FDC_DEBUG_LOG
898 emu->out_debug_log(_T("FDC\tSEEKn"));
902 void MB8877::cmd_stepout()
905 cmdtype = FDC_CMD_TYPE1;
906 status = FDC_ST_HEADENG | FDC_ST_BUSY;
908 seektrk = (fdc[drvreg].track > 0) ? fdc[drvreg].track - 1 : 0;
911 if(fdc[drvreg].track != seektrk) {
912 cancel_my_event(EVENT_SEEKEND);
913 register_seek_event();
915 cancel_my_event(EVENT_SEEK);
916 // register_my_event(EVENT_SEEKEND, 300);
917 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
919 #ifdef _FDC_DEBUG_LOG
920 emu->out_debug_log(_T("FDC\tSEEKn"));
924 void MB8877::cmd_readdata(bool first_sector)
927 cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
928 int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;
929 status = FDC_ST_BUSY;
930 //status_tmp = search_sector(trkreg, side, secreg, ((cmdreg & 2) != 0));
931 status_tmp = search_sector();
935 if(!(status_tmp & FDC_ST_RECNFND)) {
936 time = get_usec_to_start_trans(first_sector);
938 time = disk[drvreg]->get_usec_per_track();
940 register_my_event(EVENT_SEARCH, time);
941 cancel_my_event(EVENT_LOST);
944 void MB8877::cmd_writedata(bool first_sector)
947 cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
948 status = FDC_ST_BUSY;
949 status_tmp = search_sector() & ~FDC_ST_RECTYPE;
953 if(!(status_tmp & FDC_ST_RECNFND)) {
954 time = get_usec_to_start_trans(first_sector);
956 time = disk[drvreg]->get_usec_per_track();
958 register_my_event(EVENT_SEARCH, time);
959 cancel_my_event(EVENT_LOST);
962 void MB8877::cmd_readaddr()
964 // type-3 read address
965 cmdtype = FDC_CMD_RD_ADDR;
966 status = FDC_ST_BUSY;
967 status_tmp = search_addr();
971 if(!(status_tmp & FDC_ST_RECNFND)) {
972 time = get_usec_to_start_trans(true);
974 time = disk[drvreg]->get_usec_per_track();
976 register_my_event(EVENT_SEARCH, time);
977 cancel_my_event(EVENT_LOST);
980 void MB8877::cmd_readtrack()
983 cmdtype = FDC_CMD_RD_TRK;
984 status = FDC_ST_BUSY;
987 disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
988 fdc[drvreg].index = 0;
991 fdc[drvreg].next_trans_position = 0;
992 int bytes = disk[drvreg]->get_track_size() - get_cur_position();
993 double time = disk[drvreg]->get_usec_per_bytes(bytes);
995 // wait at least 15msec before check index hole raise first drq
996 if((cmdreg & 4) && time < 15000) {
997 time += disk[drvreg]->get_usec_per_track();
999 register_my_event(EVENT_SEARCH, time);
1000 cancel_my_event(EVENT_LOST);
1003 void MB8877::cmd_writetrack()
1005 // type-3 write track
1006 cmdtype = FDC_CMD_WR_TRK;
1007 status = FDC_ST_BUSY;
1010 fdc[drvreg].index = 0;
1011 fdc[drvreg].id_written = false;
1016 // wait 15msec before raise first drq
1017 fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(15000)) % disk[drvreg]->get_track_size();
1020 // raise first drq soon
1021 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1022 time = disk[drvreg]->get_usec_per_bytes(1);
1024 // wait at least 3bytes before check index hole and raise second drq
1025 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1026 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1027 fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1029 register_my_event(EVENT_SEARCH, time);
1030 cancel_my_event(EVENT_LOST);
1033 void MB8877::cmd_forceint()
1035 // type-4 force interrupt
1036 if(cmdtype == FDC_CMD_WR_TRK) {
1037 if(!disk[drvreg]->write_protected) {
1038 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1039 // data mark of last sector is not written
1040 disk[drvreg]->set_data_mark_missing();
1042 disk[drvreg]->sync_buffer();
1045 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1047 cmdtype = FDC_CMD_TYPE1;
1049 status &= ~FDC_ST_BUSY;
1051 // force interrupt if bit0-bit3 is high
1056 // finish current seeking
1058 if(seektrk > fdc[drvreg].track) {
1059 fdc[drvreg].track++;
1060 } else if(seektrk < fdc[drvreg].track) {
1061 fdc[drvreg].track--;
1063 if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
1064 trkreg = fdc[drvreg].track;
1066 if(seektrk == fdc[drvreg].track) {
1068 if((cmdreg_tmp & 0xf0) == 0) {
1073 now_search = now_seek = false;
1075 cancel_my_event(EVENT_SEEK);
1076 cancel_my_event(EVENT_SEEKEND);
1077 cancel_my_event(EVENT_SEARCH);
1078 cancel_my_event(EVENT_DRQ);
1079 cancel_my_event(EVENT_MULTI1);
1080 cancel_my_event(EVENT_MULTI2);
1081 cancel_my_event(EVENT_LOST);
1084 // ----------------------------------------------------------------------------
1086 // ----------------------------------------------------------------------------
1088 uint8 MB8877::search_track()
1091 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)){
1092 return FDC_ST_SEEKERR;
1095 // verify track number
1096 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1097 disk[drvreg]->get_sector(-1, -1, i);
1098 if(disk[drvreg]->id[0] == trkreg) {
1102 return FDC_ST_SEEKERR;
1105 uint8 MB8877::search_sector()
1108 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1110 return FDC_ST_RECNFND;
1113 // get current position
1114 int sector_num = disk[drvreg]->sector_num.sd;
1115 int position = get_cur_position();
1117 if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1118 position -= disk[drvreg]->get_track_size();
1121 // first scanned sector
1122 int first_sector = 0;
1123 for(int i = 0; i < sector_num; i++) {
1124 if(position < disk[drvreg]->sync_position[i]) {
1131 for(int i = 0; i < sector_num; i++) {
1133 int index = (first_sector + i) % sector_num;
1134 disk[drvreg]->get_sector(-1, -1, index);
1137 // if(disk[drvreg]->id[0] != trkreg) {
1140 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1143 if(disk[drvreg]->id[2] != secreg) {
1146 if(disk[drvreg]->sector_size.sd == 0) {
1149 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1151 disk[drvreg]->sector_size.sd = 0;
1153 return FDC_ST_RECNFND | FDC_ST_CRCERR;
1157 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1158 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1159 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[i] - fdc[drvreg].next_trans_position;
1161 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i];
1163 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[i];
1164 fdc[drvreg].index = 0;
1165 #ifdef _FDC_DEBUG_LOG
1166 emu->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1167 disk[drvreg]->sector_size.sd,
1168 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1169 disk[drvreg]->id[4], disk[drvreg]->id[5],
1170 disk[drvreg]->crc_error ? 1 : 0);
1172 //return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0) | ((disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0);
1173 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1177 disk[drvreg]->sector_size.sd = 0;
1179 return FDC_ST_RECNFND;
1182 uint8 MB8877::search_addr()
1185 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1187 return FDC_ST_RECNFND;
1190 // get current position
1191 int sector_num = disk[drvreg]->sector_num.sd;
1192 int position = get_cur_position();
1194 if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1195 position -= disk[drvreg]->get_track_size();
1198 // first scanned sector
1199 int first_sector = 0;
1200 for(int i = 0; i < sector_num; i++) {
1201 if(position < disk[drvreg]->sync_position[i]) {
1208 if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1209 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector];
1210 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[first_sector];
1211 fdc[drvreg].index = 0;
1212 secreg = disk[drvreg]->id[0];
1213 //return (disk[drvreg]->crc_error && !config.ignore_crc[drvreg]) ? FDC_ST_CRCERR : 0;
1214 //return (disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0;
1219 disk[drvreg]->sector_size.sd = 0;
1221 return FDC_ST_RECNFND;
1224 // ----------------------------------------------------------------------------
1226 // ----------------------------------------------------------------------------
1228 int MB8877::get_cur_position()
1230 return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1233 double MB8877::get_usec_to_start_trans(bool first_sector)
1235 #if defined(_X1TURBO) || defined(_X1TURBOZ)
1236 // FIXME: ugly patch for X1turbo ALPHA
1237 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
1241 if(disk[drvreg]->no_skew && !disk[drvreg]->correct_timing()) {
1242 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1243 // so use the constant period to search the target sector
1247 // get time from current position
1248 int position = get_cur_position();
1249 int bytes = fdc[drvreg].next_trans_position - position;
1250 if(fdc[drvreg].next_sync_position < position || bytes < 0) {
1251 bytes += disk[drvreg]->get_track_size();
1253 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1257 // wait at least 15msec before search target sector and rqise first drq
1258 if((cmdreg & 4) && time < 15000) {
1259 time += disk[drvreg]->get_usec_per_track();
1261 // wait at least 60msec before search target sector and rqise first drq just after seek command is done or drive register is written
1262 if(time < 60000 - passed_usec(seekend_clock)) {
1263 time += disk[drvreg]->get_usec_per_track();
1269 // ----------------------------------------------------------------------------
1271 // ----------------------------------------------------------------------------
1273 void MB8877::set_irq(bool val)
1275 write_signals(&outputs_irq, val ? 0xffffffff : 0);
1278 void MB8877::set_drq(bool val)
1280 write_signals(&outputs_drq, val ? 0xffffffff : 0);
1283 // ----------------------------------------------------------------------------
1285 // ----------------------------------------------------------------------------
1287 void MB8877::open_disk(int drv, _TCHAR path[], int bank)
1289 if(drv < MAX_DRIVE) {
1290 disk[drv]->open(path, bank);
1291 #ifdef _FDC_DEBUG_LOG
1292 emu->out_debug_log(_T("FDC\tOPEN DISK#%d\n"), drv);
1297 void MB8877::close_disk(int drv)
1299 if(drv < MAX_DRIVE) {
1302 #ifdef _FDC_DEBUG_LOG
1303 emu->out_debug_log(_T("FDC\tCLOSE DISK#%d\n"), drv);
1308 bool MB8877::disk_inserted(int drv)
1310 if(drv < MAX_DRIVE) {
1311 return disk[drv]->inserted;
1316 void MB8877::set_disk_protected(int drv, bool value)
1318 if(drv < MAX_DRIVE) {
1319 disk[drv]->write_protected = value;
1323 bool MB8877::get_disk_protected(int drv)
1325 if(drv < MAX_DRIVE) {
1326 return disk[drv]->write_protected;
1331 void MB8877::set_drive_type(int drv, uint8 type)
1333 if(drv < MAX_DRIVE) {
1334 disk[drv]->drive_type = type;
1338 uint8 MB8877::get_drive_type(int drv)
1340 if(drv < MAX_DRIVE) {
1341 return disk[drv]->drive_type;
1343 return DRIVE_TYPE_UNK;
1346 void MB8877::set_drive_rpm(int drv, int rpm)
1348 if(drv < MAX_DRIVE) {
1349 disk[drv]->drive_rpm = rpm;
1353 void MB8877::set_drive_mfm(int drv, bool mfm)
1355 if(drv < MAX_DRIVE) {
1356 disk[drv]->drive_mfm = mfm;
1360 uint8 MB8877::fdc_status()
1362 // for each virtual machines
1363 #if defined(_FMR50) || defined(_FMR60)
1364 return disk[drvreg]->inserted ? 2 : 0;
1370 #define STATE_VERSION 4
1372 void MB8877::save_state(FILEIO* state_fio)
1374 state_fio->FputUint32(STATE_VERSION);
1375 state_fio->FputInt32(this_device_id);
1377 state_fio->Fwrite(fdc, sizeof(fdc), 1);
1378 for(int i = 0; i < MAX_DRIVE; i++) {
1379 disk[i]->save_state(state_fio);
1381 state_fio->FputUint8(status);
1382 state_fio->FputUint8(status_tmp);
1383 state_fio->FputUint8(cmdreg);
1384 state_fio->FputUint8(cmdreg_tmp);
1385 state_fio->FputUint8(trkreg);
1386 state_fio->FputUint8(secreg);
1387 state_fio->FputUint8(datareg);
1388 state_fio->FputUint8(drvreg);
1389 state_fio->FputUint8(sidereg);
1390 state_fio->FputUint8(cmdtype);
1391 state_fio->Fwrite(register_id, sizeof(register_id), 1);
1392 state_fio->FputBool(now_search);
1393 state_fio->FputBool(now_seek);
1394 state_fio->FputInt32(no_command);
1395 state_fio->FputInt32(seektrk);
1396 state_fio->FputBool(seekvct);
1397 state_fio->FputBool(motor_on);
1398 state_fio->FputBool(drive_sel);
1399 state_fio->FputUint32(prev_drq_clock);
1400 state_fio->FputUint32(seekend_clock);
1403 bool MB8877::load_state(FILEIO* state_fio)
1405 if(state_fio->FgetUint32() != STATE_VERSION) {
1408 if(state_fio->FgetInt32() != this_device_id) {
1411 state_fio->Fread(fdc, sizeof(fdc), 1);
1412 for(int i = 0; i < MAX_DRIVE; i++) {
1413 if(!disk[i]->load_state(state_fio)) {
1417 status = state_fio->FgetUint8();
1418 status_tmp = state_fio->FgetUint8();
1419 cmdreg = state_fio->FgetUint8();
1420 cmdreg_tmp = state_fio->FgetUint8();
1421 trkreg = state_fio->FgetUint8();
1422 secreg = state_fio->FgetUint8();
1423 datareg = state_fio->FgetUint8();
1424 drvreg = state_fio->FgetUint8();
1425 sidereg = state_fio->FgetUint8();
1426 cmdtype = state_fio->FgetUint8();
1427 state_fio->Fread(register_id, sizeof(register_id), 1);
1428 now_search = state_fio->FgetBool();
1429 now_seek = state_fio->FgetBool();
1430 no_command = state_fio->FgetInt32();
1431 seektrk = state_fio->FgetInt32();
1432 seekvct = state_fio->FgetBool();
1433 motor_on = state_fio->FgetBool();
1434 drive_sel = state_fio->FgetBool();
1435 prev_drq_clock = state_fio->FgetUint32();
1436 seekend_clock = state_fio->FgetUint32();