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(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
403 status |= FDC_ST_WRITEP;
405 status &= ~FDC_ST_WRITEP;
407 // track0, index hole
408 if(cmdtype == FDC_CMD_TYPE1) {
409 if(fdc[drvreg].track == 0) {
410 status |= FDC_ST_TRACK00;
412 status &= ~FDC_ST_TRACK00;
414 // index hole signal width is 5msec (thanks Mr.Sato)
415 if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
416 status |= FDC_ST_INDEX;
418 status &= ~FDC_ST_INDEX;
421 // show busy a moment
423 if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
424 status &= ~FDC_ST_BUSY;
425 #ifdef MB8877_NO_BUSY_AFTER_SEEK
430 if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
431 // MZ-2000 HuBASIC invites NOT READY status
432 if(++no_command == 16) {
433 val |= FDC_ST_NOTREADY;
439 if(!(status & FDC_ST_DRQ)) {
442 if(!(status & FDC_ST_BUSY)) {
445 #ifdef _FDC_DEBUG_LOG
446 //emu->out_debug_log(_T("FDCSTATUS=%2x\n"), val);
449 return (~val) & 0xff;
456 return (~trkreg) & 0xff;
463 return (~secreg) & 0xff;
469 if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
470 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
471 // read or multisector read
472 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
473 datareg = disk[drvreg]->sector[fdc[drvreg].index];
474 //fdc[drvreg].index++;
476 if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
478 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
480 #ifdef _FDC_DEBUG_LOG
481 emu->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
483 status |= FDC_ST_CRCERR;
484 status &= ~FDC_ST_BUSY;
487 } else if(cmdtype == FDC_CMD_RD_SEC) {
489 #ifdef _FDC_DEBUG_LOG
490 emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
492 status &= ~FDC_ST_BUSY;
497 #ifdef _FDC_DEBUG_LOG
498 emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
500 register_my_event(EVENT_MULTI1, 30);
501 register_my_event(EVENT_MULTI2, 60);
504 register_drq_event(1);
506 status &= ~FDC_ST_DRQ;
507 } else if(cmdtype == FDC_CMD_RD_ADDR) {
509 if(fdc[drvreg].index < 6) {
510 datareg = disk[drvreg]->id[fdc[drvreg].index];
511 //fdc[drvreg].index++;
513 if((fdc[drvreg].index + 1) >= 6) {
514 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
516 status |= FDC_ST_CRCERR;
518 status &= ~FDC_ST_BUSY;
521 #ifdef _FDC_DEBUG_LOG
522 emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
525 register_drq_event(1);
527 status &= ~FDC_ST_DRQ;
528 } else if(cmdtype == FDC_CMD_RD_TRK) {
530 if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
531 datareg = disk[drvreg]->track[fdc[drvreg].index];
532 //fdc[drvreg].index++;
534 if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
535 #ifdef _FDC_DEBUG_LOG
536 emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));
538 status &= ~FDC_ST_BUSY;
539 status |= FDC_ST_LOSTDATA;
542 #ifdef _FDC_DEBUG_LOG
543 emu->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
546 register_drq_event(1);
548 status &= ~FDC_ST_DRQ;
550 if(!(status & FDC_ST_DRQ)) {
551 cancel_my_event(EVENT_LOST);
553 fdc[drvreg].access = true;
556 #ifdef _FDC_DEBUG_LOG
557 //emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);
560 return (~datareg) & 0xff;
568 void MB8877::write_dma_io8(uint32 addr, uint32 data)
573 uint32 MB8877::read_dma_io8(uint32 addr)
578 void MB8877::write_signal(int id, uint32 data, uint32 mask)
580 if(id == SIG_MB8877_DRIVEREG) {
581 drvreg = data & DRIVE_MASK;
583 seekend_clock = current_clock();
584 } else if(id == SIG_MB8877_SIDEREG) {
585 sidereg = (data & mask) ? 1 : 0;
586 } else if(id == SIG_MB8877_MOTOR) {
587 motor_on = ((data & mask) != 0);
591 uint32 MB8877::read_signal(int ch)
596 for(int i = 0; i < MAX_DRIVE; i++) {
600 fdc[i].access = false;
608 void MB8877::event_callback(int event_id, int err)
610 int event = event_id >> 8;
611 int cmd = event_id & 0xff;
612 register_id[event] = -1;
614 // cancel event if the command is finished or other command is executed
616 if(event == EVENT_SEEK) {
618 } else if(event == EVENT_SEARCH) {
626 #ifdef _FDC_DEBUG_LOG
627 //emu->out_debug_log(_T("FDC\tSEEK START\n"));
629 if(seektrk > fdc[drvreg].track) {
631 } else if(seektrk < fdc[drvreg].track) {
634 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
635 trkreg = fdc[drvreg].track;
637 if(seektrk == fdc[drvreg].track) {
639 //if((cmdreg & 0xf0) == 0) {
643 status |= search_track();
646 seekend_clock = current_clock();
648 #ifdef _FDC_DEBUG_LOG
649 emu->out_debug_log(_T("FDC\tSEEK\n"));
652 register_seek_event();
656 if(seektrk == fdc[drvreg].track) {
658 if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
659 trkreg = fdc[drvreg].track;
661 //if((cmdreg & 0xf0) == 0) {
665 status |= search_track();
668 seekend_clock = current_clock();
669 // cancel_my_event(EVENT_SEEK);
671 #ifdef _FDC_DEBUG_LOG
672 emu->out_debug_log(_T("FDC\tSEEK END\n"));
678 if(status_tmp & FDC_ST_RECNFND) {
679 #if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
680 // for SHARP X1 Batten Tanuki
681 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
682 status_tmp &= ~FDC_ST_RECNFND;
685 #ifdef _FDC_DEBUG_LOG
686 emu->out_debug_log(_T("FDC\tSEARCH NG\n"));
688 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
691 } else if(status_tmp & FDC_ST_WRITEFAULT) {
692 #ifdef _FDC_DEBUG_LOG
693 emu->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
695 status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
699 status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
700 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
701 register_lost_event(8);
702 } else if(cmdtype == FDC_CMD_WR_TRK) {
703 register_lost_event(3);
705 register_lost_event(1);
707 fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
708 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
711 #ifdef _FDC_DEBUG_LOG
712 emu->out_debug_log(_T("FDC\tSEARCH OK\n"));
717 if(status & FDC_ST_BUSY) {
718 status |= FDC_ST_DRQ;
719 register_lost_event(1);
720 if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
721 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
723 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
725 if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
726 cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
727 cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK ||
728 cmdtype == FDC_CMD_RD_ADDR) {
731 fdc[drvreg].prev_clock = prev_drq_clock = current_clock();
733 #ifdef _FDC_DEBUG_LOG
734 //emu->out_debug_log(_T("FDC\tDRQ!\n"));
742 if(cmdtype == FDC_CMD_RD_MSEC) {
744 } else if(cmdtype == FDC_CMD_WR_MSEC) {
745 cmd_writedata(false);
749 if(status & FDC_ST_BUSY) {
750 #ifdef _FDC_DEBUG_LOG
751 emu->out_debug_log("FDC\tDATA LOST\n");
753 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
754 if(fdc[drvreg].index == 0) {
755 status &= ~FDC_ST_BUSY;
756 //status &= ~FDC_ST_DRQ;
766 status |= FDC_ST_LOSTDATA;
772 // ----------------------------------------------------------------------------
774 // ----------------------------------------------------------------------------
776 void MB8877::process_cmd()
778 #ifdef _FDC_DEBUG_LOG
779 static const _TCHAR *cmdstr[0x10] = {
780 _T("RESTORE "), _T("SEEK "), _T("STEP "), _T("STEP "),
781 _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
782 _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
783 _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
785 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);
789 switch(cmdreg & 0xf0) {
837 void MB8877::cmd_restore()
840 cmdtype = FDC_CMD_TYPE1;
841 status = FDC_ST_HEADENG | FDC_ST_BUSY;
846 if(fdc[drvreg].track != seektrk) {
847 cancel_my_event(EVENT_SEEKEND);
848 register_seek_event();
850 cancel_my_event(EVENT_SEEK);
851 // register_my_event(EVENT_SEEKEND, 300);
852 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
855 #ifdef _FDC_DEBUG_LOG
856 emu->out_debug_log(_T("FDC\tSEEKn"));
860 void MB8877::cmd_seek()
863 cmdtype = FDC_CMD_TYPE1;
864 status = FDC_ST_HEADENG | FDC_ST_BUSY;
867 seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
868 seekvct = !(datareg > trkreg);
870 if(fdc[drvreg].track != seektrk) {
871 cancel_my_event(EVENT_SEEKEND);
872 register_seek_event();
874 cancel_my_event(EVENT_SEEK);
875 // register_my_event(EVENT_SEEKEND, 300);
876 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
878 #ifdef _FDC_DEBUG_LOG
879 emu->out_debug_log(_T("FDC\tSEEKn"));
883 void MB8877::cmd_step()
893 void MB8877::cmd_stepin()
896 cmdtype = FDC_CMD_TYPE1;
897 status = FDC_ST_HEADENG | FDC_ST_BUSY;
899 seektrk = (fdc[drvreg].track < 83) ? fdc[drvreg].track + 1 : 83;
902 if(fdc[drvreg].track != seektrk) {
903 cancel_my_event(EVENT_SEEKEND);
904 register_seek_event();
906 cancel_my_event(EVENT_SEEK);
907 // register_my_event(EVENT_SEEKEND, 300);
908 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
910 #ifdef _FDC_DEBUG_LOG
911 emu->out_debug_log(_T("FDC\tSEEKn"));
915 void MB8877::cmd_stepout()
918 cmdtype = FDC_CMD_TYPE1;
919 status = FDC_ST_HEADENG | FDC_ST_BUSY;
921 seektrk = (fdc[drvreg].track > 0) ? fdc[drvreg].track - 1 : 0;
924 if(fdc[drvreg].track != seektrk) {
925 cancel_my_event(EVENT_SEEKEND);
926 register_seek_event();
928 cancel_my_event(EVENT_SEEK);
929 // register_my_event(EVENT_SEEKEND, 300);
930 register_my_event(EVENT_SEEKEND, 300.0 * 1000.0);
932 #ifdef _FDC_DEBUG_LOG
933 emu->out_debug_log(_T("FDC\tSEEKn"));
937 void MB8877::cmd_readdata(bool first_sector)
940 cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
941 int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;
942 status = FDC_ST_BUSY;
943 //status_tmp = search_sector(trkreg, side, secreg, ((cmdreg & 2) != 0));
944 status_tmp = search_sector();
948 if(status_tmp & FDC_ST_RECNFND) {
949 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
950 time = disk[drvreg]->get_usec_per_bytes(bytes);
952 time = get_usec_to_start_trans(first_sector);
954 register_my_event(EVENT_SEARCH, time);
955 cancel_my_event(EVENT_LOST);
958 void MB8877::cmd_writedata(bool first_sector)
961 cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
962 status = FDC_ST_BUSY;
963 status_tmp = search_sector() & ~FDC_ST_RECTYPE;
967 if(status_tmp & FDC_ST_RECNFND) {
968 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
969 time = disk[drvreg]->get_usec_per_bytes(bytes);
970 } else if(status & FDC_ST_WRITEFAULT) {
971 time = (cmdreg & 4) ? 15000 : 1;
973 time = get_usec_to_start_trans(first_sector);
975 register_my_event(EVENT_SEARCH, time);
976 cancel_my_event(EVENT_LOST);
979 void MB8877::cmd_readaddr()
981 // type-3 read address
982 cmdtype = FDC_CMD_RD_ADDR;
983 status = FDC_ST_BUSY;
984 status_tmp = search_addr();
988 if(status_tmp & FDC_ST_RECNFND) {
989 int bytes = disk[drvreg]->get_track_size() * 5 - get_cur_position();
990 time = disk[drvreg]->get_usec_per_bytes(bytes);
992 time = get_usec_to_start_trans(true);
994 register_my_event(EVENT_SEARCH, time);
995 cancel_my_event(EVENT_LOST);
998 void MB8877::cmd_readtrack()
1000 // type-3 read track
1001 cmdtype = FDC_CMD_RD_TRK;
1002 status = FDC_ST_BUSY;
1005 disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
1006 fdc[drvreg].index = 0;
1009 fdc[drvreg].next_trans_position = 0;
1010 int bytes = disk[drvreg]->get_track_size() - get_cur_position();
1011 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1013 // wait at least 15msec before check index hole raise first drq
1014 if((cmdreg & 4) && time < 15000) {
1015 time += disk[drvreg]->get_usec_per_track();
1017 register_my_event(EVENT_SEARCH, time);
1018 cancel_my_event(EVENT_LOST);
1021 void MB8877::cmd_writetrack()
1023 // type-3 write track
1024 cmdtype = FDC_CMD_WR_TRK;
1025 status = FDC_ST_BUSY;
1028 fdc[drvreg].index = 0;
1029 fdc[drvreg].id_written = false;
1033 if(disk[drvreg]->write_protected) {
1034 status_tmp = FDC_ST_WRITEFAULT;
1035 time = (cmdreg & 4) ? 15000 : 1;
1038 // wait 15msec before raise first drq
1039 fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(15000)) % disk[drvreg]->get_track_size();
1042 // raise first drq soon
1043 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
1044 time = disk[drvreg]->get_usec_per_bytes(1);
1046 // wait at least 3bytes before check index hole and raise second drq
1047 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
1048 if(fdc[drvreg].bytes_before_2nd_drq < 3) {
1049 fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
1052 register_my_event(EVENT_SEARCH, time);
1053 cancel_my_event(EVENT_LOST);
1056 void MB8877::cmd_forceint()
1058 // type-4 force interrupt
1059 if(cmdtype == FDC_CMD_WR_TRK) {
1060 if(!disk[drvreg]->write_protected) {
1061 if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
1062 // data mark of last sector is not written
1063 disk[drvreg]->set_data_mark_missing();
1065 disk[drvreg]->sync_buffer();
1068 if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
1069 cmdtype = FDC_CMD_TYPE1;
1070 status = FDC_ST_HEADENG;
1072 status &= ~FDC_ST_BUSY;
1074 // force interrupt if bit0-bit3 is high
1079 // finish current seeking
1081 if(seektrk > fdc[drvreg].track) {
1082 fdc[drvreg].track++;
1083 } else if(seektrk < fdc[drvreg].track) {
1084 fdc[drvreg].track--;
1086 if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
1087 trkreg = fdc[drvreg].track;
1089 if(seektrk == fdc[drvreg].track) {
1091 if((cmdreg_tmp & 0xf0) == 0) {
1096 now_search = now_seek = false;
1098 cancel_my_event(EVENT_SEEK);
1099 cancel_my_event(EVENT_SEEKEND);
1100 cancel_my_event(EVENT_SEARCH);
1101 cancel_my_event(EVENT_DRQ);
1102 cancel_my_event(EVENT_MULTI1);
1103 cancel_my_event(EVENT_MULTI2);
1104 cancel_my_event(EVENT_LOST);
1107 // ----------------------------------------------------------------------------
1109 // ----------------------------------------------------------------------------
1111 uint8 MB8877::search_track()
1114 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)){
1115 return FDC_ST_SEEKERR;
1118 // verify track number
1119 if(disk[drvreg]->ignore_crc()) {
1120 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1121 disk[drvreg]->get_sector(-1, -1, i);
1122 if(disk[drvreg]->id[0] == trkreg) {
1127 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1128 disk[drvreg]->get_sector(-1, -1, i);
1129 if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
1133 for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
1134 disk[drvreg]->get_sector(-1, -1, i);
1135 if(disk[drvreg]->id[0] == trkreg) {
1136 return FDC_ST_SEEKERR | FDC_ST_CRCERR;
1140 return FDC_ST_SEEKERR;
1143 uint8 MB8877::search_sector()
1146 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1147 if(disk[drvreg]->write_protected) {
1148 return FDC_ST_WRITEFAULT;
1153 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1154 return FDC_ST_RECNFND;
1157 // get current position
1158 int sector_num = disk[drvreg]->sector_num.sd;
1159 int position = get_cur_position();
1161 if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1162 position -= disk[drvreg]->get_track_size();
1165 // first scanned sector
1166 int first_sector = 0;
1167 for(int i = 0; i < sector_num; i++) {
1168 if(position < disk[drvreg]->sync_position[i]) {
1175 for(int i = 0; i < sector_num; i++) {
1177 int index = (first_sector + i) % sector_num;
1178 disk[drvreg]->get_sector(-1, -1, index);
1181 // if(disk[drvreg]->id[0] != trkreg) {
1184 if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
1187 if(disk[drvreg]->id[2] != secreg) {
1190 if(disk[drvreg]->sector_size.sd == 0) {
1193 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
1195 disk[drvreg]->sector_size.sd = 0;
1196 return FDC_ST_RECNFND | FDC_ST_CRCERR;
1200 if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
1201 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
1202 fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[i] - fdc[drvreg].next_trans_position;
1204 fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i];
1206 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[i];
1207 fdc[drvreg].index = 0;
1208 #ifdef _FDC_DEBUG_LOG
1209 emu->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
1210 disk[drvreg]->sector_size.sd,
1211 disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
1212 disk[drvreg]->id[4], disk[drvreg]->id[5],
1213 disk[drvreg]->crc_error ? 1 : 0);
1215 //return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0) | ((disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0);
1216 return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
1220 disk[drvreg]->sector_size.sd = 0;
1221 return FDC_ST_RECNFND;
1224 uint8 MB8877::search_addr()
1227 if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
1228 return FDC_ST_RECNFND;
1231 // get current position
1232 int sector_num = disk[drvreg]->sector_num.sd;
1233 int position = get_cur_position();
1235 if(position > disk[drvreg]->sync_position[sector_num - 1]) {
1236 position -= disk[drvreg]->get_track_size();
1239 // first scanned sector
1240 int first_sector = 0;
1241 for(int i = 0; i < sector_num; i++) {
1242 if(position < disk[drvreg]->sync_position[i]) {
1249 if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
1250 fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector];
1251 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[first_sector];
1252 fdc[drvreg].index = 0;
1253 secreg = disk[drvreg]->id[0];
1254 //return (disk[drvreg]->crc_error && !config.ignore_crc[drvreg]) ? FDC_ST_CRCERR : 0;
1255 //return (disk[drvreg]->crc_error && !disk[drvreg]->ignore_crc()) ? FDC_ST_CRCERR : 0;
1260 disk[drvreg]->sector_size.sd = 0;
1261 return FDC_ST_RECNFND;
1264 // ----------------------------------------------------------------------------
1266 // ----------------------------------------------------------------------------
1268 int MB8877::get_cur_position()
1270 return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
1273 double MB8877::get_usec_to_start_trans(bool first_sector)
1275 #if defined(_X1TURBO) || defined(_X1TURBOZ)
1276 // FIXME: ugly patch for X1turbo ALPHA
1277 if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1TURBO_ALPHA) {
1281 if(disk[drvreg]->no_skew && !disk[drvreg]->correct_timing()) {
1282 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
1283 // so use the constant period to search the target sector
1287 // get time from current position
1288 int position = get_cur_position();
1289 int bytes = fdc[drvreg].next_trans_position - position;
1290 if(fdc[drvreg].next_sync_position < position || bytes < 0) {
1291 bytes += disk[drvreg]->get_track_size();
1293 double time = disk[drvreg]->get_usec_per_bytes(bytes);
1297 // wait at least 15msec before search target sector and rqise first drq
1298 if((cmdreg & 4) && time < 15000) {
1299 time += disk[drvreg]->get_usec_per_track();
1301 // wait at least 60msec before search target sector and rqise first drq just after seek command is done or drive register is written
1302 if(time < 60000 - passed_usec(seekend_clock)) {
1303 time += disk[drvreg]->get_usec_per_track();
1309 // ----------------------------------------------------------------------------
1311 // ----------------------------------------------------------------------------
1313 void MB8877::set_irq(bool val)
1315 write_signals(&outputs_irq, val ? 0xffffffff : 0);
1318 void MB8877::set_drq(bool val)
1320 write_signals(&outputs_drq, val ? 0xffffffff : 0);
1323 // ----------------------------------------------------------------------------
1325 // ----------------------------------------------------------------------------
1327 void MB8877::open_disk(int drv, _TCHAR path[], int bank)
1329 if(drv < MAX_DRIVE) {
1330 disk[drv]->open(path, bank);
1331 #ifdef _FDC_DEBUG_LOG
1332 emu->out_debug_log(_T("FDC\tOPEN DISK#%d\n"), drv);
1337 void MB8877::close_disk(int drv)
1339 if(drv < MAX_DRIVE) {
1342 #ifdef _FDC_DEBUG_LOG
1343 emu->out_debug_log(_T("FDC\tCLOSE DISK#%d\n"), drv);
1348 bool MB8877::disk_inserted(int drv)
1350 if(drv < MAX_DRIVE) {
1351 return disk[drv]->inserted;
1356 void MB8877::set_disk_protected(int drv, bool value)
1358 if(drv < MAX_DRIVE) {
1359 disk[drv]->write_protected = value;
1363 bool MB8877::get_disk_protected(int drv)
1365 if(drv < MAX_DRIVE) {
1366 return disk[drv]->write_protected;
1371 void MB8877::set_drive_type(int drv, uint8 type)
1373 if(drv < MAX_DRIVE) {
1374 disk[drv]->drive_type = type;
1378 uint8 MB8877::get_drive_type(int drv)
1380 if(drv < MAX_DRIVE) {
1381 return disk[drv]->drive_type;
1383 return DRIVE_TYPE_UNK;
1386 void MB8877::set_drive_rpm(int drv, int rpm)
1388 if(drv < MAX_DRIVE) {
1389 disk[drv]->drive_rpm = rpm;
1393 void MB8877::set_drive_mfm(int drv, bool mfm)
1395 if(drv < MAX_DRIVE) {
1396 disk[drv]->drive_mfm = mfm;
1400 uint8 MB8877::fdc_status()
1402 // for each virtual machines
1403 #if defined(_FMR50) || defined(_FMR60)
1404 return disk[drvreg]->inserted ? 2 : 0;
1410 #define STATE_VERSION 4
1412 void MB8877::save_state(FILEIO* state_fio)
1414 state_fio->FputUint32(STATE_VERSION);
1415 state_fio->FputInt32(this_device_id);
1417 state_fio->Fwrite(fdc, sizeof(fdc), 1);
1418 for(int i = 0; i < MAX_DRIVE; i++) {
1419 disk[i]->save_state(state_fio);
1421 state_fio->FputUint8(status);
1422 state_fio->FputUint8(status_tmp);
1423 state_fio->FputUint8(cmdreg);
1424 state_fio->FputUint8(cmdreg_tmp);
1425 state_fio->FputUint8(trkreg);
1426 state_fio->FputUint8(secreg);
1427 state_fio->FputUint8(datareg);
1428 state_fio->FputUint8(drvreg);
1429 state_fio->FputUint8(sidereg);
1430 state_fio->FputUint8(cmdtype);
1431 state_fio->Fwrite(register_id, sizeof(register_id), 1);
1432 state_fio->FputBool(now_search);
1433 state_fio->FputBool(now_seek);
1434 state_fio->FputInt32(no_command);
1435 state_fio->FputInt32(seektrk);
1436 state_fio->FputBool(seekvct);
1437 state_fio->FputBool(motor_on);
1438 state_fio->FputBool(drive_sel);
1439 state_fio->FputUint32(prev_drq_clock);
1440 state_fio->FputUint32(seekend_clock);
1443 bool MB8877::load_state(FILEIO* state_fio)
1445 if(state_fio->FgetUint32() != STATE_VERSION) {
1448 if(state_fio->FgetInt32() != this_device_id) {
1451 state_fio->Fread(fdc, sizeof(fdc), 1);
1452 for(int i = 0; i < MAX_DRIVE; i++) {
1453 if(!disk[i]->load_state(state_fio)) {
1457 status = state_fio->FgetUint8();
1458 status_tmp = state_fio->FgetUint8();
1459 cmdreg = state_fio->FgetUint8();
1460 cmdreg_tmp = state_fio->FgetUint8();
1461 trkreg = state_fio->FgetUint8();
1462 secreg = state_fio->FgetUint8();
1463 datareg = state_fio->FgetUint8();
1464 drvreg = state_fio->FgetUint8();
1465 sidereg = state_fio->FgetUint8();
1466 cmdtype = state_fio->FgetUint8();
1467 state_fio->Fread(register_id, sizeof(register_id), 1);
1468 now_search = state_fio->FgetBool();
1469 now_seek = state_fio->FgetBool();
1470 no_command = state_fio->FgetInt32();
1471 seektrk = state_fio->FgetInt32();
1472 seekvct = state_fio->FgetBool();
1473 motor_on = state_fio->FgetBool();
1474 drive_sel = state_fio->FgetBool();
1475 prev_drq_clock = state_fio->FgetUint32();
1476 seekend_clock = state_fio->FgetUint32();