OSDN Git Service

[VM][FM8][FM77] Add delay FIRQ/NMI from 2HD/SFD FDC.This is temporally implement.
[csp-qt/common_source_project-fm7.git] / source / src / vm / mb8877.cpp
index 0f7cbd8..d186b5c 100644 (file)
-/*\r
-       Skelton for retropc emulator\r
-\r
-       Origin : XM7\r
-       Author : Takeda.Toshiya\r
-       Date   : 2006.12.06 -\r
-\r
-       [ MB8877 / MB8876 ]\r
-*/\r
-\r
-#include "mb8877.h"\r
-#include "disk.h"\r
-#include "../fileio.h"\r
-\r
-#define FDC_ST_BUSY            0x01    // busy\r
-#define FDC_ST_INDEX           0x02    // index hole\r
-#define FDC_ST_DRQ             0x02    // data request\r
-#define FDC_ST_TRACK00         0x04    // track0\r
-#define FDC_ST_LOSTDATA                0x04    // data lost\r
-#define FDC_ST_CRCERR          0x08    // crc error\r
-#define FDC_ST_SEEKERR         0x10    // seek error\r
-#define FDC_ST_RECNFND         0x10    // sector not found\r
-#define FDC_ST_HEADENG         0x20    // head engage\r
-#define FDC_ST_RECTYPE         0x20    // record type\r
-#define FDC_ST_WRITEFAULT      0x20    // write fault\r
-#define FDC_ST_WRITEP          0x40    // write protectdc\r
-#define FDC_ST_NOTREADY                0x80    // media not inserted\r
-\r
-#define FDC_CMD_TYPE1          1\r
-#define FDC_CMD_RD_SEC         2\r
-#define FDC_CMD_RD_MSEC                3\r
-#define FDC_CMD_WR_SEC         4\r
-#define FDC_CMD_WR_MSEC                5\r
-#define FDC_CMD_RD_ADDR                6\r
-#define FDC_CMD_RD_TRK         7\r
-#define FDC_CMD_WR_TRK         8\r
-#define FDC_CMD_TYPE4          0x80\r
-\r
-#define EVENT_SEEK             0\r
-#define EVENT_SEEKEND          1\r
-#define EVENT_SEARCH           2\r
-#define EVENT_TYPE4            3\r
-#define EVENT_DRQ              4\r
-#define EVENT_MULTI1           5\r
-#define EVENT_MULTI2           6\r
-#define EVENT_LOST             7\r
-\r
-#define DRIVE_MASK             (MAX_DRIVE - 1)\r
-\r
-static const int seek_wait_hi[4] = {3000,  6000, 10000, 16000};\r
-static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000};\r
-\r
-#define CANCEL_EVENT(event) { \\r
-       if(register_id[event] != -1) { \\r
-               cancel_event(this, register_id[event]); \\r
-               register_id[event] = -1; \\r
-       } \\r
-}\r
-#define REGISTER_EVENT(event, usec) { \\r
-       if(register_id[event] != -1) { \\r
-               cancel_event(this, register_id[event]); \\r
-               register_id[event] = -1; \\r
-       } \\r
-       register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, &register_id[event]); \\r
-}\r
-#define REGISTER_SEEK_EVENT() { \\r
-       if(register_id[EVENT_SEEK] != -1) { \\r
-               cancel_event(this, register_id[EVENT_SEEK]); \\r
-               register_id[EVENT_SEEK] = -1; \\r
-       } \\r
-       if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) { \\r
-               register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, &register_id[EVENT_SEEK]); \\r
-       } else { \\r
-               register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, &register_id[EVENT_SEEK]); \\r
-       } \\r
-       now_seek = after_seek = true; \\r
-}\r
-#define REGISTER_DRQ_EVENT() { \\r
-       double usec = disk[drvreg]->get_usec_per_bytes(1) - passed_usec(prev_drq_clock); \\r
-       if(usec < 4) { \\r
-               usec = 4; \\r
-       } else if(usec > 24 && disk[drvreg]->is_alpha) { \\r
-               usec = 24; \\r
-       } \\r
-       if(register_id[EVENT_DRQ] != -1) { \\r
-               cancel_event(this, register_id[EVENT_DRQ]); \\r
-               register_id[EVENT_DRQ] = -1; \\r
-       } \\r
-       register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, &register_id[EVENT_DRQ]); \\r
-}\r
-#define REGISTER_LOST_EVENT() { \\r
-       if(register_id[EVENT_LOST] != -1) { \\r
-               cancel_event(this, register_id[EVENT_LOST]); \\r
-               register_id[EVENT_LOST] = -1; \\r
-       } \\r
-       register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(1), false, &register_id[EVENT_LOST]); \\r
-}\r
-\r
-void MB8877::initialize()\r
-{\r
-       // config\r
-       ignore_crc = config.ignore_crc;\r
-       \r
-       // initialize d88 handler\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               disk[i] = new DISK();\r
-       }\r
-       \r
-       // initialize timing\r
-       memset(fdc, 0, sizeof(fdc));\r
-       \r
-       // initialize fdc\r
-       seektrk = 0;\r
-       seekvct = true;\r
-       status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;\r
-       drvreg = 0;\r
-}\r
-\r
-void MB8877::release()\r
-{\r
-       // release d88 handler\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               delete disk[i];\r
-       }\r
-}\r
-\r
-void MB8877::reset()\r
-{\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               fdc[i].track = 0;\r
-               fdc[i].index = 0;\r
-               fdc[i].access = false;\r
-       }\r
-       for(int i = 0; i < array_length(register_id); i++) {\r
-               register_id[i] = -1;\r
-       }\r
-       now_search = now_seek = after_seek = drive_sel = false;\r
-       no_command = 0;\r
-}\r
-\r
-void MB8877::update_config()\r
-{\r
-       ignore_crc = config.ignore_crc;\r
-}\r
-\r
-void MB8877::write_io8(uint32 addr, uint32 data)\r
-{\r
-       switch(addr & 3) {\r
-       case 0:\r
-               // command reg\r
-               cmdreg_tmp = cmdreg;\r
-#ifdef HAS_MB8876\r
-               cmdreg = (~data) & 0xff;\r
-#else\r
-               cmdreg = data;\r
-#endif\r
-               process_cmd();\r
-               no_command = 0;\r
-               break;\r
-       case 1:\r
-               // track reg\r
-#ifdef HAS_MB8876\r
-               trkreg = (~data) & 0xff;\r
-#else\r
-               trkreg = data;\r
-#endif\r
-               if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {\r
-                       // track reg is written after command starts\r
-                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {\r
-                               process_cmd();\r
-                       }\r
-               }\r
-               break;\r
-       case 2:\r
-               // sector reg\r
-#ifdef HAS_MB8876\r
-               secreg = (~data) & 0xff;\r
-#else\r
-               secreg = data;\r
-#endif\r
-               if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {\r
-                       // sector reg is written after command starts\r
-                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {\r
-                               process_cmd();\r
-                       }\r
-               }\r
-               break;\r
-       case 3:\r
-               // data reg\r
-#ifdef HAS_MB8876\r
-               datareg = (~data) & 0xff;\r
-#else\r
-               datareg = data;\r
-#endif\r
-               if(motor_on && (status & FDC_ST_DRQ) && !now_search) {\r
-                       if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {\r
-                               // write or multisector write\r
-                               if(fdc[drvreg].index < disk[drvreg]->sector_size) {\r
-                                       if(!disk[drvreg]->write_protected) {\r
-                                               disk[drvreg]->sector[fdc[drvreg].index] = datareg;\r
-                                               // dm, ddm\r
-                                               disk[drvreg]->deleted = (cmdreg & 1) ? 0x10 : 0;\r
-                                       } else {\r
-                                               status |= FDC_ST_WRITEFAULT;\r
-                                               status &= ~FDC_ST_BUSY;\r
-                                               cmdtype = 0;\r
-                                               set_irq(true);\r
-                                       }\r
-                                       fdc[drvreg].index++;\r
-                               }\r
-                               if(fdc[drvreg].index >= disk[drvreg]->sector_size) {\r
-                                       if(cmdtype == FDC_CMD_WR_SEC) {\r
-                                               // single sector\r
-                                               status &= ~FDC_ST_BUSY;\r
-                                               cmdtype = 0;\r
-                                               set_irq(true);\r
-                                       } else {\r
-                                               // multisector\r
-                                               REGISTER_EVENT(EVENT_MULTI1, 30);\r
-                                               REGISTER_EVENT(EVENT_MULTI2, 60);\r
-                                       }\r
-                               } else if(status & FDC_ST_DRQ) {\r
-                                       REGISTER_DRQ_EVENT();\r
-                               }\r
-                               status &= ~FDC_ST_DRQ;\r
-                       } else if(cmdtype == FDC_CMD_WR_TRK) {\r
-                               // read track\r
-                               if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {\r
-                                       if(!disk[drvreg]->write_protected) {\r
-                                               disk[drvreg]->track[fdc[drvreg].index] = datareg;\r
-                                       } else {\r
-                                               status |= FDC_ST_WRITEFAULT;\r
-                                               status &= ~FDC_ST_BUSY;\r
-                                               status &= ~FDC_ST_DRQ;\r
-                                               cmdtype = 0;\r
-                                               set_irq(true);\r
-                                       }\r
-                                       fdc[drvreg].index++;\r
-                               }\r
-                               if(fdc[drvreg].index >= disk[drvreg]->get_track_size()) {\r
-                                       status &= ~FDC_ST_BUSY;\r
-                                       cmdtype = 0;\r
-                                       set_irq(true);\r
-                               } else if(status & FDC_ST_DRQ) {\r
-                                       REGISTER_DRQ_EVENT();\r
-                               }\r
-                               status &= ~FDC_ST_DRQ;\r
-                       }\r
-                       if(!(status & FDC_ST_DRQ)) {\r
-                               CANCEL_EVENT(EVENT_LOST);\r
-                               set_drq(false);\r
-                               fdc[drvreg].access = true;\r
-                       }\r
-               }\r
-               break;\r
-       }\r
-}\r
-\r
-uint32 MB8877::read_io8(uint32 addr)\r
-{\r
-       uint32 val;\r
-       \r
-       switch(addr & 3) {\r
-       case 0:\r
-               // status reg\r
-               if(cmdtype == FDC_CMD_TYPE4) {\r
-                       // now force interrupt\r
-                       if(!disk[drvreg]->inserted || !motor_on) {\r
-                               status = FDC_ST_NOTREADY;\r
-                       } else {\r
-                               // MZ-2500 RELICS invites STATUS = 0\r
-                               status = 0;\r
-                       }\r
-                       val = status;\r
-               } else if(now_search) {\r
-                       // now sector search\r
-                       val = FDC_ST_BUSY;\r
-               } else {\r
-                       // disk not inserted, motor stop\r
-                       if(!disk[drvreg]->inserted || !motor_on) {\r
-                               status |= FDC_ST_NOTREADY;\r
-                       } else {\r
-                               status &= ~FDC_ST_NOTREADY;\r
-                       }\r
-                       // write protected\r
-                       if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {\r
-                               if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {\r
-                                       status |= FDC_ST_WRITEP;\r
-                               } else {\r
-                                       status &= ~FDC_ST_WRITEP;\r
-                               }\r
-                       } else {\r
-                               status &= ~FDC_ST_WRITEP;\r
-                       }\r
-                       // track0, index hole\r
-                       if(cmdtype == FDC_CMD_TYPE1) {\r
-                               if(fdc[drvreg].track == 0) {\r
-                                       status |= FDC_ST_TRACK00;\r
-                               } else {\r
-                                       status &= ~FDC_ST_TRACK00;\r
-                               }\r
-                               if(!(status & FDC_ST_NOTREADY)) {\r
-                                       if(get_cur_position() == 0) {\r
-                                               status |= FDC_ST_INDEX;\r
-                                       } else {\r
-                                               status &= ~FDC_ST_INDEX;\r
-                                       }\r
-                               }\r
-                       }\r
-                       // show busy a moment\r
-                       val = status;\r
-                       if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {\r
-                               status &= ~FDC_ST_BUSY;\r
-                       }\r
-               }\r
-               if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {\r
-                       // MZ-2000 HuBASIC invites NOT READY status\r
-                       if(++no_command == 16) {\r
-                               val |= FDC_ST_NOTREADY;\r
-                       }\r
-               } else {\r
-                       no_command = 0;\r
-               }\r
-               // reset irq/drq\r
-               if(!(status & FDC_ST_DRQ)) {\r
-                       set_drq(false);\r
-               }\r
-               set_irq(false);\r
-#ifdef _FDC_DEBUG_LOG\r
-               emu->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);\r
-#endif\r
-#ifdef HAS_MB8876\r
-               return (~val) & 0xff;\r
-#else\r
-               return val;\r
-#endif\r
-       case 1:\r
-               // track reg\r
-#ifdef HAS_MB8876\r
-               return (~trkreg) & 0xff;\r
-#else\r
-               return trkreg;\r
-#endif\r
-       case 2:\r
-               // sector reg\r
-#ifdef HAS_MB8876\r
-               return (~secreg) & 0xff;\r
-#else\r
-               return secreg;\r
-#endif\r
-       case 3:\r
-               // data reg\r
-               if(motor_on && (status & FDC_ST_DRQ) && !now_search) {\r
-                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {\r
-                               // read or multisector read\r
-                               if(fdc[drvreg].index < disk[drvreg]->sector_size) {\r
-                                       datareg = disk[drvreg]->sector[fdc[drvreg].index];\r
-                                       fdc[drvreg].index++;\r
-                               }\r
-                               if(fdc[drvreg].index >= disk[drvreg]->sector_size) {\r
-                                       if(cmdtype == FDC_CMD_RD_SEC) {\r
-                                               // single sector\r
-#ifdef _FDC_DEBUG_LOG\r
-                                               emu->out_debug_log(_T("FDC\tEND OF SECTOR\n"));\r
-#endif\r
-                                               status &= ~FDC_ST_BUSY;\r
-                                               cmdtype = 0;\r
-                                               set_irq(true);\r
-                                       } else {\r
-                                               // multisector\r
-#ifdef _FDC_DEBUG_LOG\r
-                                               emu->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));\r
-#endif\r
-                                               REGISTER_EVENT(EVENT_MULTI1, 30);\r
-                                               REGISTER_EVENT(EVENT_MULTI2, 60);\r
-                                       }\r
-                               } else {\r
-                                       REGISTER_DRQ_EVENT();\r
-                               }\r
-                               status &= ~FDC_ST_DRQ;\r
-                       } else if(cmdtype == FDC_CMD_RD_ADDR) {\r
-                               // read address\r
-                               if(fdc[drvreg].index < 6) {\r
-                                       datareg = disk[drvreg]->id[fdc[drvreg].index];\r
-                                       fdc[drvreg].index++;\r
-                               }\r
-                               if(fdc[drvreg].index >= 6) {\r
-                                       status &= ~FDC_ST_BUSY;\r
-                                       cmdtype = 0;\r
-                                       set_irq(true);\r
-                               } else {\r
-                                       REGISTER_DRQ_EVENT();\r
-                               }\r
-                               status &= ~FDC_ST_DRQ;\r
-                       } else if(cmdtype == FDC_CMD_RD_TRK) {\r
-                               // read track\r
-                               if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {\r
-                                       datareg = disk[drvreg]->track[fdc[drvreg].index];\r
-                                       fdc[drvreg].index++;\r
-                               }\r
-                               if(fdc[drvreg].index >= disk[drvreg]->get_track_size()) {\r
-#ifdef _FDC_DEBUG_LOG\r
-                                       emu->out_debug_log(_T("FDC\tEND OF TRACK\n"));\r
-#endif\r
-                                       status &= ~FDC_ST_BUSY;\r
-                                       status |= FDC_ST_LOSTDATA;\r
-                                       cmdtype = 0;\r
-                                       set_irq(true);\r
-                               } else {\r
-                                       REGISTER_DRQ_EVENT();\r
-                               }\r
-                               status &= ~FDC_ST_DRQ;\r
-                       }\r
-                       if(!(status & FDC_ST_DRQ)) {\r
-                               CANCEL_EVENT(EVENT_LOST);\r
-                               set_drq(false);\r
-                               fdc[drvreg].access = true;\r
-                       }\r
-               }\r
-#ifdef _FDC_DEBUG_LOG\r
-               emu->out_debug_log(_T("FDC\tDATA=%2x\n"), datareg);\r
-#endif\r
-#ifdef HAS_MB8876\r
-               return (~datareg) & 0xff;\r
-#else\r
-               return datareg;\r
-#endif\r
-       }\r
-       return 0xff;\r
-}\r
-\r
-void MB8877::write_dma_io8(uint32 addr, uint32 data)\r
-{\r
-       write_io8(3, data);\r
-}\r
-\r
-uint32 MB8877::read_dma_io8(uint32 addr)\r
-{\r
-       return read_io8(3);\r
-}\r
-\r
-void MB8877::write_signal(int id, uint32 data, uint32 mask)\r
-{\r
-       if(id == SIG_MB8877_DRIVEREG) {\r
-               drvreg = data & DRIVE_MASK;\r
-               drive_sel = true;\r
-       } else if(id == SIG_MB8877_SIDEREG) {\r
-               sidereg = (data & mask) ? 1 : 0;\r
-       } else if(id == SIG_MB8877_MOTOR) {\r
-               motor_on = ((data & mask) != 0);\r
-       }\r
-}\r
-\r
-uint32 MB8877::read_signal(int ch)\r
-{\r
-       // get access status\r
-       uint32 stat = 0;\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               if(fdc[i].access) {\r
-                       stat |= 1 << i;\r
-               }\r
-               fdc[i].access = false;\r
-       }\r
-       if(now_search) {\r
-               stat |= 1 << drvreg;\r
-       }\r
-       return stat;\r
-}\r
-\r
-void MB8877::event_callback(int event_id, int err)\r
-{\r
-       int event = event_id >> 8;\r
-       int cmd = event_id & 0xff;\r
-       register_id[event] = -1;\r
-       \r
-       // cancel event if the command is finished or other command is executed\r
-       if(cmd != cmdtype) {\r
-               if(event == EVENT_SEEK) {\r
-                       now_seek = false;\r
-               } else if(event == EVENT_SEARCH) {\r
-                       now_search = false;\r
-               }\r
-               return;\r
-       }\r
-       \r
-       switch(event) {\r
-       case EVENT_SEEK:\r
-               if(seektrk > fdc[drvreg].track) {\r
-                       fdc[drvreg].track++;\r
-               } else if(seektrk < fdc[drvreg].track) {\r
-                       fdc[drvreg].track--;\r
-               }\r
-               if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {\r
-                       trkreg = fdc[drvreg].track;\r
-               }\r
-               if(seektrk == fdc[drvreg].track) {\r
-                       // auto update\r
-                       if((cmdreg & 0xf0) == 0) {\r
-                               datareg = 0;\r
-                       }\r
-                       status |= search_track();\r
-                       now_seek = false;\r
-                       set_irq(true);\r
-               } else {\r
-                       REGISTER_SEEK_EVENT();\r
-               }\r
-               break;\r
-       case EVENT_SEEKEND:\r
-               if(seektrk == fdc[drvreg].track) {\r
-                       // auto update\r
-                       if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {\r
-                               trkreg = fdc[drvreg].track;\r
-                       }\r
-                       if((cmdreg & 0xf0) == 0) {\r
-                               datareg = 0;\r
-                       }\r
-                       status |= search_track();\r
-                       now_seek = false;\r
-                       CANCEL_EVENT(EVENT_SEEK);\r
-                       set_irq(true);\r
-               }\r
-               break;\r
-       case EVENT_SEARCH:\r
-               now_search = false;\r
-               if(!(status_tmp & FDC_ST_RECNFND)) {\r
-                       status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);\r
-                       REGISTER_LOST_EVENT();\r
-                       fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;\r
-                       fdc[drvreg].prev_clock = prev_drq_clock = current_clock();\r
-                       set_drq(true);\r
-                       drive_sel = false;\r
-               } else {\r
-#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)\r
-                       // for SHARP X1 Batten Tanuki\r
-                       if(drive_sel) {\r
-                               status_tmp &= ~FDC_ST_RECNFND;\r
-                       }\r
-#endif\r
-                       status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);\r
-               }\r
-               break;\r
-       case EVENT_TYPE4:\r
-               cmdtype = FDC_CMD_TYPE4;\r
-               break;\r
-       case EVENT_DRQ:\r
-               if(status & FDC_ST_BUSY) {\r
-                       status |= FDC_ST_DRQ;\r
-                       REGISTER_LOST_EVENT();\r
-                       fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();\r
-                       fdc[drvreg].prev_clock = prev_drq_clock = current_clock();\r
-                       set_drq(true);\r
-               }\r
-               break;\r
-       case EVENT_MULTI1:\r
-               secreg++;\r
-               break;\r
-       case EVENT_MULTI2:\r
-               if(cmdtype == FDC_CMD_RD_MSEC) {\r
-                       cmd_readdata();\r
-               } else if(cmdtype == FDC_CMD_WR_MSEC) {\r
-                       cmd_writedata();\r
-               }\r
-               break;\r
-       case EVENT_LOST:\r
-               if(status & FDC_ST_BUSY) {\r
-#ifdef _FDC_DEBUG_LOG\r
-                       emu->out_debug_log("FDC\tDATA LOST\n");\r
-#endif\r
-                       status |= FDC_ST_LOSTDATA;\r
-                       status &= ~FDC_ST_BUSY;\r
-                       //status &= ~FDC_ST_DRQ;\r
-                       set_irq(true);\r
-                       //set_drq(false);\r
-               }\r
-               break;\r
-       }\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// command\r
-// ----------------------------------------------------------------------------\r
-\r
-void MB8877::process_cmd()\r
-{\r
-#ifdef _FDC_DEBUG_LOG\r
-       static const _TCHAR *cmdstr[0x10] = {\r
-               _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),\r
-               _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),\r
-               _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),\r
-               _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")\r
-       };\r
-       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);\r
-#endif\r
-       \r
-       CANCEL_EVENT(EVENT_TYPE4);\r
-       set_irq(false);\r
-       \r
-       switch(cmdreg & 0xf0) {\r
-       // type-1\r
-       case 0x00:\r
-               cmd_restore();\r
-               break;\r
-       case 0x10:\r
-               cmd_seek();\r
-               break;\r
-       case 0x20:\r
-       case 0x30:\r
-               cmd_step();\r
-               break;\r
-       case 0x40:\r
-       case 0x50:\r
-               cmd_stepin();\r
-               break;\r
-       case 0x60:\r
-       case 0x70:\r
-               cmd_stepout();\r
-               break;\r
-       // type-2\r
-       case 0x80:\r
-       case 0x90:\r
-               cmd_readdata();\r
-               break;\r
-       case 0xa0:\r
-       case 0xb0:\r
-               cmd_writedata();\r
-               break;\r
-       // type-3\r
-       case 0xc0:\r
-               cmd_readaddr();\r
-               break;\r
-       case 0xe0:\r
-               cmd_readtrack();\r
-               break;\r
-       case 0xf0:\r
-               cmd_writetrack();\r
-               break;\r
-       // type-4\r
-       case 0xd0:\r
-               cmd_forceint();\r
-               break;\r
-       default:\r
-               break;\r
-       }\r
-}\r
-\r
-void MB8877::cmd_restore()\r
-{\r
-       // type-1 restore\r
-       cmdtype = FDC_CMD_TYPE1;\r
-       status = FDC_ST_HEADENG | FDC_ST_BUSY;\r
-       trkreg = 0xff;\r
-       \r
-       seektrk = 0;\r
-       seekvct = true;\r
-       \r
-       REGISTER_SEEK_EVENT();\r
-       REGISTER_EVENT(EVENT_SEEKEND, 300);\r
-}\r
-\r
-void MB8877::cmd_seek()\r
-{\r
-       // type-1 seek\r
-       cmdtype = FDC_CMD_TYPE1;\r
-       status = FDC_ST_HEADENG | FDC_ST_BUSY;\r
-       \r
-#if 0\r
-       seektrk = fdc[drvreg].track + datareg - trkreg;\r
-#else\r
-       seektrk = datareg;\r
-#endif\r
-       seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;\r
-       seekvct = !(datareg > trkreg);\r
-       \r
-       REGISTER_SEEK_EVENT();\r
-       REGISTER_EVENT(EVENT_SEEKEND, 300);\r
-}\r
-\r
-void MB8877::cmd_step()\r
-{\r
-       // type-1 step\r
-       if(seekvct) {\r
-               cmd_stepout();\r
-       } else {\r
-               cmd_stepin();\r
-       }\r
-}\r
-\r
-void MB8877::cmd_stepin()\r
-{\r
-       // type-1 step in\r
-       cmdtype = FDC_CMD_TYPE1;\r
-       status = FDC_ST_HEADENG | FDC_ST_BUSY;\r
-       \r
-       seektrk = (fdc[drvreg].track < 83) ? fdc[drvreg].track + 1 : 83;\r
-       seekvct = false;\r
-       \r
-       REGISTER_SEEK_EVENT();\r
-       REGISTER_EVENT(EVENT_SEEKEND, 300);\r
-}\r
-\r
-void MB8877::cmd_stepout()\r
-{\r
-       // type-1 step out\r
-       cmdtype = FDC_CMD_TYPE1;\r
-       status = FDC_ST_HEADENG | FDC_ST_BUSY;\r
-       \r
-       seektrk = (fdc[drvreg].track > 0) ? fdc[drvreg].track - 1 : 0;\r
-       seekvct = true;\r
-       \r
-       REGISTER_SEEK_EVENT();\r
-       REGISTER_EVENT(EVENT_SEEKEND, 300);\r
-}\r
-\r
-void MB8877::cmd_readdata()\r
-{\r
-       // type-2 read data\r
-       cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;\r
-       int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;\r
-       status = FDC_ST_BUSY;\r
-       status_tmp = search_sector(fdc[drvreg].track, side, secreg, ((cmdreg & 2) != 0));\r
-       now_search = true;\r
-       \r
-       double time;\r
-        printf("Disk: READDATA\n");\r
-       if(!(status_tmp & FDC_ST_RECNFND)) {\r
-               time = get_usec_to_start_trans();\r
-       } else {\r
-               time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());\r
-       }\r
-       REGISTER_EVENT(EVENT_SEARCH, time);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-}\r
-\r
-void MB8877::cmd_writedata()\r
-{\r
-       // type-2 write data\r
-       cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;\r
-       int side = (cmdreg & 2) ? ((cmdreg & 8) ? 1 : 0) : sidereg;\r
-       status = FDC_ST_BUSY;\r
-       status_tmp = search_sector(fdc[drvreg].track, side, secreg, ((cmdreg & 2) != 0)) & ~FDC_ST_RECTYPE;\r
-       now_search = true;\r
-       \r
-       double time;\r
-       if(!(status_tmp & FDC_ST_RECNFND)) {\r
-               time = get_usec_to_start_trans();\r
-       } else {\r
-               time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());\r
-       }\r
-       REGISTER_EVENT(EVENT_SEARCH, time);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-}\r
-\r
-void MB8877::cmd_readaddr()\r
-{\r
-       // type-3 read address\r
-       cmdtype = FDC_CMD_RD_ADDR;\r
-       status = FDC_ST_BUSY;\r
-       status_tmp = search_addr();\r
-       now_search = true;\r
-       \r
-       double time;\r
-       if(!(status_tmp & FDC_ST_RECNFND)) {\r
-               time = get_usec_to_start_trans();\r
-       } else {\r
-               time = disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());\r
-       }\r
-       REGISTER_EVENT(EVENT_SEARCH, time);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-}\r
-\r
-void MB8877::cmd_readtrack()\r
-{\r
-       // type-3 read track\r
-       cmdtype = FDC_CMD_RD_TRK;\r
-       status = FDC_ST_BUSY;\r
-       status_tmp = 0;\r
-       \r
-       disk[drvreg]->make_track(fdc[drvreg].track, sidereg);\r
-       fdc[drvreg].index = 0;\r
-       now_search = true;\r
-       \r
-       int bytes = disk[drvreg]->get_track_size() - get_cur_position();\r
-       if(bytes < 12) {\r
-               bytes += disk[drvreg]->get_track_size();\r
-       }\r
-       double time = disk[drvreg]->get_usec_per_bytes(bytes);\r
-       REGISTER_EVENT(EVENT_SEARCH, time);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-}\r
-\r
-void MB8877::cmd_writetrack()\r
-{\r
-       // type-3 write track\r
-       cmdtype = FDC_CMD_WR_TRK;\r
-       status = FDC_ST_BUSY;\r
-       status_tmp = 0;\r
-       \r
-       fdc[drvreg].index = 0;\r
-       now_search = true;\r
-       \r
-       int bytes = disk[drvreg]->get_track_size() - get_cur_position();\r
-       if(bytes < 12) {\r
-               bytes += disk[drvreg]->get_track_size();\r
-       }\r
-       double time = disk[drvreg]->get_usec_per_bytes(bytes);\r
-       REGISTER_EVENT(EVENT_SEARCH, time);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-}\r
-\r
-void MB8877::cmd_forceint()\r
-{\r
-       // type-4 force interrupt\r
-#if 0\r
-       if(!disk[drvreg]->inserted || !motor_on) {\r
-               status = FDC_ST_NOTREADY | FDC_ST_HEADENG;\r
-       } else {\r
-               status = FDC_ST_HEADENG;\r
-       }\r
-       cmdtype = FDC_CMD_TYPE4;\r
-#else\r
-//     if(cmdtype == 0 || cmdtype == 4) {              // modified for mz-2800, why in the write sector command case?\r
-       if(cmdtype == 0 || cmdtype == FDC_CMD_TYPE4) {  // is this correct?\r
-               status = 0;\r
-               cmdtype = FDC_CMD_TYPE1;\r
-       }\r
-       status &= ~FDC_ST_BUSY;\r
-#endif\r
-       \r
-       // force interrupt if bit0-bit3 is high\r
-       if(cmdreg & 0x0f) {\r
-               set_irq(true);\r
-       }\r
-       \r
-       // finish current seeking\r
-       if(now_seek) {\r
-               if(seektrk > fdc[drvreg].track) {\r
-                       fdc[drvreg].track++;\r
-               } else if(seektrk < fdc[drvreg].track) {\r
-                       fdc[drvreg].track--;\r
-               }\r
-               if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {\r
-                       trkreg = fdc[drvreg].track;\r
-               }\r
-               if(seektrk == fdc[drvreg].track) {\r
-                       // auto update\r
-                       if((cmdreg_tmp & 0xf0) == 0) {\r
-                               datareg = 0;\r
-                       }\r
-               }\r
-       }\r
-       now_search = now_seek = false;\r
-       \r
-       CANCEL_EVENT(EVENT_SEEK);\r
-       CANCEL_EVENT(EVENT_SEEKEND);\r
-       CANCEL_EVENT(EVENT_SEARCH);\r
-       CANCEL_EVENT(EVENT_TYPE4);\r
-       CANCEL_EVENT(EVENT_DRQ);\r
-       CANCEL_EVENT(EVENT_MULTI1);\r
-       CANCEL_EVENT(EVENT_MULTI2);\r
-       CANCEL_EVENT(EVENT_LOST);\r
-       REGISTER_EVENT(EVENT_TYPE4, 100);\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// media handler\r
-// ----------------------------------------------------------------------------\r
-\r
-uint8 MB8877::search_track()\r
-{\r
-       int trk = fdc[drvreg].track;\r
-       \r
-       if(!disk[drvreg]->get_track(trk, sidereg)) {\r
-               return FDC_ST_SEEKERR;\r
-       }\r
-       \r
-       // verify track number\r
-       if(!(cmdreg & 4)) {\r
-               return 0;\r
-       }\r
-       for(int i = 0; i < disk[drvreg]->sector_num; i++) {\r
-               disk[drvreg]->get_sector(trk, sidereg, i);\r
-               if(disk[drvreg]->id[0] == trkreg) {\r
-                       return 0;\r
-               }\r
-       }\r
-       return FDC_ST_SEEKERR;\r
-}\r
-\r
-uint8 MB8877::search_sector(int trk, int side, int sct, bool compare)\r
-{\r
-       // get track\r
-       if(!disk[drvreg]->get_track(trk, side)) {\r
-               set_irq(true);\r
-               return FDC_ST_RECNFND;\r
-       }\r
-       \r
-       // get current position\r
-       int sector_num = disk[drvreg]->sector_num;\r
-       int position = get_cur_position();\r
-       \r
-       if(position > disk[drvreg]->sync_position[sector_num - 1]) {\r
-               position -= disk[drvreg]->get_track_size();\r
-       }\r
-       \r
-       // first scanned sector\r
-       int first_sector = 0;\r
-       for(int i = 0; i < sector_num; i++) {\r
-               if(position < disk[drvreg]->sync_position[i]) {\r
-                       first_sector = i;\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       // scan sectors\r
-       for(int i = 0; i < sector_num; i++) {\r
-               // get sector\r
-               int index = (first_sector + i) % sector_num;\r
-               disk[drvreg]->get_sector(trk, side, index);\r
-               \r
-               // check id\r
-               if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {\r
-                       continue;\r
-               }\r
-               if(disk[drvreg]->id[2] != sct) {\r
-                       continue;\r
-               }\r
-               \r
-               // sector found\r
-               fdc[drvreg].next_trans_position = disk[drvreg]->data_position[i];\r
-               fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[i];\r
-               fdc[drvreg].index = 0;\r
-               return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0) | ((disk[drvreg]->status && !ignore_crc) ? FDC_ST_CRCERR : 0);\r
-       }\r
-       \r
-       // sector not found\r
-       disk[drvreg]->sector_size = 0;\r
-       set_irq(true);\r
-       return FDC_ST_RECNFND;\r
-}\r
-\r
-uint8 MB8877::search_addr()\r
-{\r
-       int trk = fdc[drvreg].track;\r
-       \r
-       // get track\r
-       if(!disk[drvreg]->get_track(trk, sidereg)) {\r
-               set_irq(true);\r
-               return FDC_ST_RECNFND;\r
-       }\r
-       \r
-       // get current position\r
-       int sector_num = disk[drvreg]->sector_num;\r
-       int position = get_cur_position();\r
-       \r
-       if(position > disk[drvreg]->sync_position[sector_num - 1]) {\r
-               position -= disk[drvreg]->get_track_size();\r
-       }\r
-       \r
-       // first scanned sector\r
-       int first_sector = 0;\r
-       for(int i = 0; i < sector_num; i++) {\r
-               if(position < disk[drvreg]->sync_position[i]) {\r
-                       first_sector = i;\r
-                       break;\r
-               }\r
-       }\r
-       \r
-       // get sector\r
-       if(disk[drvreg]->get_sector(trk, sidereg, first_sector)) {\r
-               fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector];\r
-               fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[first_sector];\r
-               fdc[drvreg].index = 0;\r
-               secreg = disk[drvreg]->id[0];\r
-               return (disk[drvreg]->status && !ignore_crc) ? FDC_ST_CRCERR : 0;\r
-       }\r
-       \r
-       // sector not found\r
-       disk[drvreg]->sector_size = 0;\r
-       set_irq(true);\r
-       return FDC_ST_RECNFND;\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// timing\r
-// ----------------------------------------------------------------------------\r
-\r
-int MB8877::get_cur_position()\r
-{\r
-       return (int)(fdc[drvreg].cur_position + passed_usec(fdc[drvreg].prev_clock) / disk[drvreg]->get_usec_per_bytes(1)) % disk[drvreg]->get_track_size();\r
-}\r
-\r
-double MB8877::get_usec_to_start_trans()\r
-{\r
-#if defined(_X1TURBO) || defined(_X1TURBOZ)\r
-       // FIXME: ugly patch for X1turbo ALPHA\r
-       if(disk[drvreg]->is_alpha) {\r
-               return 100;\r
-       } else\r
-#endif\r
-       if(disk[drvreg]->no_skew) {\r
-               // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,\r
-               // so use the constant period to search the target sector\r
-               return 50000;\r
-       }\r
-       \r
-       // get time from current position\r
-       int position = get_cur_position();\r
-       int bytes = fdc[drvreg].next_trans_position - position;\r
-       if(fdc[drvreg].next_sync_position < position) {\r
-               bytes += disk[drvreg]->get_track_size();\r
-       }\r
-       double time = disk[drvreg]->get_usec_per_bytes(bytes);\r
-       if(after_seek) {\r
-               // wait 70msec to read/write data just after seek command is done\r
-//             if(time < 70000) {\r
-//                     time += disk[drvreg]->get_usec_per_bytes(disk[drvreg]->get_track_size());\r
-//             }\r
-               after_seek = false;\r
-       }\r
-       return time;\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// irq / drq\r
-// ----------------------------------------------------------------------------\r
-\r
-void MB8877::set_irq(bool val)\r
-{\r
-       write_signals(&outputs_irq, val ? 0xffffffff : 0);\r
-}\r
-\r
-void MB8877::set_drq(bool val)\r
-{\r
-       write_signals(&outputs_drq, val ? 0xffffffff : 0);\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// user interface\r
-// ----------------------------------------------------------------------------\r
-\r
-void MB8877::open_disk(int drv, _TCHAR path[], int offset)\r
-{\r
-        printf("Opened : %s drive %d\n", path, drv);\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->open(path, offset);\r
-       }\r
-}\r
-\r
-void MB8877::close_disk(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->close();\r
-               cmdtype = 0;\r
-       }\r
-}\r
-\r
-bool MB8877::disk_inserted(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               return disk[drv]->inserted;\r
-       }\r
-       return false;\r
-}\r
-\r
-void MB8877::set_drive_type(int drv, uint8 type)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->drive_type = type;\r
-       }\r
-}\r
-\r
-uint8 MB8877::get_drive_type(int drv)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               return disk[drv]->drive_type;\r
-       }\r
-       return DRIVE_TYPE_UNK;\r
-}\r
-\r
-void MB8877::set_drive_rpm(int drv, int rpm)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->drive_rpm = rpm;\r
-       }\r
-}\r
-\r
-void MB8877::set_drive_mfm(int drv, bool mfm)\r
-{\r
-       if(drv < MAX_DRIVE) {\r
-               disk[drv]->drive_mfm = mfm;\r
-       }\r
-}\r
-\r
-uint8 MB8877::fdc_status()\r
-{\r
-       // for each virtual machines\r
-#if defined(_FMR50) || defined(_FMR60)\r
-       return disk[drvreg]->inserted ? 2 : 0;\r
-#else\r
-       return 0;\r
-#endif\r
-}\r
-\r
-#define STATE_VERSION  1\r
-\r
-void MB8877::save_state(FILEIO* state_fio)\r
-{\r
-       state_fio->FputUint32(STATE_VERSION);\r
-       state_fio->FputInt32(this_device_id);\r
-       \r
-       state_fio->FputBool(ignore_crc);\r
-       state_fio->Fwrite(fdc, sizeof(fdc), 1);\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               disk[i]->save_state(state_fio);\r
-       }\r
-       state_fio->FputUint8(status);\r
-       state_fio->FputUint8(status_tmp);\r
-       state_fio->FputUint8(cmdreg);\r
-       state_fio->FputUint8(cmdreg_tmp);\r
-       state_fio->FputUint8(trkreg);\r
-       state_fio->FputUint8(secreg);\r
-       state_fio->FputUint8(datareg);\r
-       state_fio->FputUint8(drvreg);\r
-       state_fio->FputUint8(sidereg);\r
-       state_fio->FputUint8(cmdtype);\r
-       state_fio->Fwrite(register_id, sizeof(register_id), 1);\r
-       state_fio->FputBool(now_search);\r
-       state_fio->FputBool(now_seek);\r
-       state_fio->FputBool(after_seek);\r
-       state_fio->FputInt32(no_command);\r
-       state_fio->FputInt32(seektrk);\r
-       state_fio->FputBool(seekvct);\r
-       state_fio->FputBool(motor_on);\r
-       state_fio->FputBool(drive_sel);\r
-       state_fio->FputUint32(prev_drq_clock);\r
-}\r
-\r
-bool MB8877::load_state(FILEIO* state_fio)\r
-{\r
-       if(state_fio->FgetUint32() != STATE_VERSION) {\r
-               return false;\r
-       }\r
-       if(state_fio->FgetInt32() != this_device_id) {\r
-               return false;\r
-       }\r
-       ignore_crc = state_fio->FgetBool();\r
-       state_fio->Fread(fdc, sizeof(fdc), 1);\r
-       for(int i = 0; i < MAX_DRIVE; i++) {\r
-               if(!disk[i]->load_state(state_fio)) {\r
-                       return false;\r
-               }\r
-       }\r
-       status = state_fio->FgetUint8();\r
-       status_tmp = state_fio->FgetUint8();\r
-       cmdreg = state_fio->FgetUint8();\r
-       cmdreg_tmp = state_fio->FgetUint8();\r
-       trkreg = state_fio->FgetUint8();\r
-       secreg = state_fio->FgetUint8();\r
-       datareg = state_fio->FgetUint8();\r
-       drvreg = state_fio->FgetUint8();\r
-       sidereg = state_fio->FgetUint8();\r
-       cmdtype = state_fio->FgetUint8();\r
-       state_fio->Fread(register_id, sizeof(register_id), 1);\r
-       now_search = state_fio->FgetBool();\r
-       now_seek = state_fio->FgetBool();\r
-       after_seek = state_fio->FgetBool();\r
-       no_command = state_fio->FgetInt32();\r
-       seektrk = state_fio->FgetInt32();\r
-       seekvct = state_fio->FgetBool();\r
-       motor_on = state_fio->FgetBool();\r
-       drive_sel = state_fio->FgetBool();\r
-       prev_drq_clock = state_fio->FgetUint32();\r
-       return true;\r
-}\r
-\r
+/*
+       Skelton for retropc emulator
+
+       Origin : XM7
+       Author : Takeda.Toshiya
+       Date   : 2006.12.06 -
+
+       [ MB8877 / MB8876 / MB8866 / MB89311 ]
+*/
+
+#include "mb8877.h"
+#include "disk.h"
+#include "noise.h"
+
+#define FDC_ST_BUSY            0x01    // busy
+#define FDC_ST_INDEX           0x02    // index hole
+#define FDC_ST_DRQ             0x02    // data request
+#define FDC_ST_TRACK00         0x04    // track0
+#define FDC_ST_LOSTDATA                0x04    // data lost
+#define FDC_ST_CRCERR          0x08    // crc error
+#define FDC_ST_SEEKERR         0x10    // seek error
+#define FDC_ST_RECNFND         0x10    // sector not found
+#define FDC_ST_HEADENG         0x20    // head engage
+#define FDC_ST_RECTYPE         0x20    // record type
+#define FDC_ST_WRITEFAULT      0x20    // write fault
+#define FDC_ST_WRITEP          0x40    // write protectdc
+#define FDC_ST_NOTREADY                0x80    // media not inserted
+
+#define FDC_CMD_TYPE1          1
+#define FDC_CMD_RD_SEC         2
+#define FDC_CMD_RD_MSEC                3
+#define FDC_CMD_WR_SEC         4
+#define FDC_CMD_WR_MSEC                5
+#define FDC_CMD_RD_ADDR                6
+#define FDC_CMD_RD_TRK         7
+#define FDC_CMD_WR_TRK         8
+
+#define EVENT_SEEK                             0
+#define EVENT_SEEKEND_VERIFY   1
+#define EVENT_SEARCH                   2
+#define EVENT_DRQ                              3
+#define EVENT_MULTI1                   4
+#define EVENT_MULTI2                   5
+#define EVENT_LOST                             6
+
+//#define DRIVE_MASK           (MAX_DRIVE - 1)
+
+#define DELAY_TIME             (disk[drvreg]->drive_type == DRIVE_TYPE_2HD ? 15000 : 30000)
+
+static const int seek_wait_hi[4] = {3000,  6000, 10000, 16000};        // 2MHz
+static const int seek_wait_lo[4] = {6000, 12000, 20000, 30000};        // 1MHz
+
+void MB8877::cancel_my_event(int event)
+{
+       if(register_id[event] != -1) {
+               cancel_event(this, register_id[event]);
+               register_id[event] = -1;
+       }
+}
+
+void MB8877::register_my_event(int event, double usec)
+{
+       cancel_my_event(event);
+       register_event(this, (event << 8) | (cmdtype & 0xff), usec, false, &register_id[event]);
+}
+
+void MB8877::register_seek_event()
+{
+       cancel_my_event(EVENT_SEEK);
+       if(fdc[drvreg].track == seektrk) {
+               register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), 1, false, &register_id[EVENT_SEEK]);
+       } else if(disk[drvreg]->drive_type == DRIVE_TYPE_2HD) {
+               register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_hi[cmdreg & 3], false, &register_id[EVENT_SEEK]);
+       } else {
+               register_event(this, (EVENT_SEEK << 8) | (cmdtype & 0xff), seek_wait_lo[cmdreg & 3], false, &register_id[EVENT_SEEK]);
+       }
+       now_seek = true;
+}
+
+void MB8877::register_drq_event(int bytes)
+{
+       double nusec = disk[drvreg]->get_usec_per_bytes(bytes); // For debug
+       double usec = nusec - get_passed_usec(prev_drq_clock);
+
+       if(usec < 4) {
+               usec = 4;
+       }
+       if(type_fm7) {
+               if((disk[drvreg]->is_special_disk == SPECIAL_DISK_FM7_GAMBLER) ||
+                  /* (disk[drvreg]->is_special_disk == SPECIAL_DISK_FM77AV_PSYOBLADE) || */
+                  (config.correct_disk_timing[drvreg])) {
+                       usec = 7;
+               }
+       }
+       this->out_debug_log("DRQ REG: %dbytes %d:%d -> %f\n", bytes, get_current_clock(), prev_drq_clock, usec);
+       cancel_my_event(EVENT_DRQ);
+       register_event(this, (EVENT_DRQ << 8) | (cmdtype & 0xff), usec, false, &register_id[EVENT_DRQ]);
+}
+
+void MB8877::register_lost_event(int bytes)
+{
+       cancel_my_event(EVENT_LOST);
+       register_event(this, (EVENT_LOST << 8) | (cmdtype & 0xff), disk[drvreg]->get_usec_per_bytes(bytes), false, &register_id[EVENT_LOST]);
+}
+
+bool MB8877::check_drive(void)
+{
+       if(drvreg >= _max_drive) {
+               status = FDC_ST_NOTREADY;
+               //set_drq(false);
+               set_irq(true);
+               return false;
+       }
+       return true;
+}
+
+bool MB8877::check_drive2(void)
+{
+       if(!check_drive()) {
+               return false;
+       }
+       if(!is_disk_inserted(drvreg & 0x7f)) {
+               status = FDC_ST_NOTREADY;
+               set_irq(true);
+               //set_drq(false);
+               return false;
+       }
+       return true;
+}
+
+void MB8877::initialize()
+{
+       DEVICE::initialize();
+       // initialize d88 handler
+       if(osd->check_feature(_T("MAX_DRIVE"))) {
+               _max_drive = osd->get_feature_int_value(_T("MAX_DRIVE"));
+       } else if(osd->check_feature(_T("MAX_FD"))) {
+               _max_drive = osd->get_feature_int_value(_T("MAX_FD"));
+       } else {
+               _max_drive = 1;
+       }
+       _drive_mask = _max_drive - 1;
+       for(int i = 0; i < _max_drive; i++) {
+               disk[i] = new DISK(emu);
+               disk[i]->set_device_name(_T("%s/Disk #%d"), this_device_name, i + 1);
+       }
+       
+       if(osd->check_feature(_T("MB8877_NO_BUSY_AFTER_SEEK"))) {
+               mb8877_no_busy_after_seek = true;
+       }
+       if(osd->check_feature(_T("_FDC_DEBUG_LOG"))) {
+               fdc_debug_log = true;
+       }
+       if(osd->check_feature(_T("HAS_MB8866"))) {
+               type_mb8866 = true;
+               invert_registers = true;
+               set_device_name(_T("MB8866 FDC"));
+       }
+       if(osd->check_feature(_T("HAS_MB8876"))) {
+               invert_registers = true;
+               set_device_name(_T("MB8876 FDC"));
+       } else if(osd->check_feature(_T("HAS_MB89311"))) {
+               type_mb89311 = true;
+               set_device_name(_T("MB89311 FDC"));
+       } else {
+               set_device_name(_T("MB8877 FDC"));
+       }               
+       if(osd->check_feature(_T("_X1")) || osd->check_feature(_T("_X1TWIN")) ||
+               osd->check_feature(_T("_X1TURBO")) || osd->check_feature(_T("_X1TURBOZ"))) {
+               type_x1 = true;
+       }
+       if(osd->check_feature(_T("_FM7")) || osd->check_feature(_T("_FM8")) ||
+               osd->check_feature(_T("_FM77_VARIANTS")) || osd->check_feature(_T("_FM77AV_VARIANTS"))) {
+               type_fm7 = true;
+               if(osd->check_feature(_T("_FM77AV40")) || osd->check_feature(_T("_FM77AV40EX")) ||
+                  osd->check_feature(_T("_FM77AV40SX")) ||
+                  osd->check_feature(_T("_FM77AV20")) || osd->check_feature(_T("_FM77AV20EX"))) {
+                       type_fm77av_2dd = true;
+               }
+       }
+       if(osd->check_feature(_T("_FMR50"))) {
+               type_fmr50 = true;
+       }
+       if(osd->check_feature(_T("_FMR60"))) {
+               type_fmr60 = true;
+       }
+       
+       // initialize noise
+       if(d_noise_seek != NULL) {
+               d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)"));
+               if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) {
+                       if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) {
+                               d_noise_seek->load_wav_file(_T("SEEK.WAV"));
+                       }
+               }
+               d_noise_seek->set_mute(!config.sound_noise_fdd);
+       }
+       if(d_noise_head_down != NULL) {
+               d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)"));
+               d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV"));
+               d_noise_head_down->set_mute(!config.sound_noise_fdd);
+       }
+       if(d_noise_head_up != NULL) {
+               d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)"));
+               d_noise_head_up->load_wav_file(_T("HEADUP.WAV"));
+               d_noise_head_up->set_mute(!config.sound_noise_fdd);
+       }
+       
+       // initialize timing
+       memset(fdc, 0, sizeof(fdc));
+       for(int i = 0; i < 16; i++) {
+               fdc[i].track = 40;  // OK?
+               fdc[i].count_immediate = false;
+       }
+       // initialize fdc
+       seektrk = 0;
+       seekvct = true;
+       status = cmdreg = trkreg = secreg = datareg = sidereg = cmdtype = 0;
+       drvreg = 0;
+       prev_drq_clock = seekend_clock = 0;
+}
+
+void MB8877::release()
+{
+       // release d88 handler
+       for(int i = 0; i < _max_drive; i++) {
+               if(disk[i]) {
+                       disk[i]->close();
+                       delete disk[i];
+               }
+       }
+}
+
+void MB8877::reset()
+{
+       for(int i = 0; i < _max_drive; i++) {
+               //fdc[i].track = 0; // Hold to track
+               fdc[i].index = 0;
+               fdc[i].access = false;
+       }
+       for(int i = 0; i < (int)array_length(register_id); i++) {
+               register_id[i] = -1;
+       }
+       now_search = now_seek = drive_sel = false;
+       no_command = 0;
+       
+//#ifdef HAS_MB89311
+       if(type_mb89311) {
+               extended_mode = true;
+       } else {
+               extended_mode = false;
+       }
+//#endif
+}
+
+void MB8877::write_io8(uint32_t addr, uint32_t data)
+{
+       bool ready;
+       
+       switch(addr & 3) {
+       case 0:
+               // command reg
+               cmdreg_tmp = cmdreg;
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+               if(invert_registers) {
+                       cmdreg = (~data) & 0xff;
+               } else {
+//#else
+                       cmdreg = data;
+               }
+//#endif
+               process_cmd();
+               no_command = 0;
+               break;
+       case 1:
+               // track reg
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+               if(invert_registers) {
+                       trkreg = (~data) & 0xff;
+               } else {
+//#else
+                       trkreg = data;
+               }
+//#endif
+               if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
+                       // track reg is written after command starts
+                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                               process_cmd();
+                       }
+               }
+               break;
+       case 2:
+               // sector reg
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+               if(invert_registers) {
+                       secreg = (~data) & 0xff;
+               } else {
+//#else
+                       secreg = data;
+               }
+//#endif
+               if((status & FDC_ST_BUSY) && (fdc[drvreg].index == 0)) {
+                       // sector reg is written after command starts
+                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                               process_cmd();
+                       }
+               }
+               break;
+       case 3:
+               // data reg
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+               if(invert_registers) {
+                       datareg = (~data) & 0xff;
+               } else {
+//#else
+                       datareg = data;
+               }
+//#endif
+               ready = ((status & FDC_ST_DRQ) && !now_search);
+//#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
+               if(type_fm7) {
+                       if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
+                               if(!motor_on) ready = false;
+                       }
+               } else 
+//#endif
+               {
+                       if(!motor_on) ready = false;
+               }
+//             if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
+               if(ready) {
+                       if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                               // write or multisector write
+                               if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
+                                       if(!disk[drvreg]->write_protected) {
+                                               if(disk[drvreg]->sector[fdc[drvreg].index] != datareg) {
+                                                       disk[drvreg]->sector[fdc[drvreg].index] = datareg;
+                                                       sector_changed = true;
+                                               }
+                                               // dm, ddm
+                                               disk[drvreg]->set_deleted((cmdreg & 1) != 0);
+                                       } else {
+                                               status |= FDC_ST_WRITEFAULT;
+                                               status &= ~FDC_ST_BUSY;
+                                               cmdtype = 0;
+                                               set_irq(true);
+                                       }
+                                       //fdc[drvreg].index++;
+                               }
+                               if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {
+                                       if(cmdtype == FDC_CMD_WR_SEC) {
+                                               // single sector
+//#ifdef _FDC_DEBUG_LOG
+                                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
+//#endif
+                                               status &= ~FDC_ST_BUSY;
+                                               cmdtype = 0;
+                                               set_irq(true);
+                                       } else {
+                                               // multisector
+//#ifdef _FDC_DEBUG_LOG
+                                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
+//#endif
+                                               // 2HD: 360rpm, 10410bytes/track -> 0.06246bytes/us
+                                               register_my_event(EVENT_MULTI1, 30); // 0.06246bytes/us * 30us = 1.8738bytes < GAP3
+                                               register_my_event(EVENT_MULTI2, 60); // 0.06246bytes/us * 60us = 3.7476bytes < GAP3
+                                       }
+                                       sector_changed = false;
+                               } else if(status & FDC_ST_DRQ) {
+                                       if(fdc[drvreg].index == 0) {
+                                               register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
+                                       } else {
+                                               register_drq_event(1);
+                                       }
+                                       if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
+                               }
+                               status &= ~FDC_ST_DRQ;
+                       } else if(cmdtype == FDC_CMD_WR_TRK) {
+                               // write track
+                               if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
+                                       if(!disk[drvreg]->write_protected) {
+                                               if(fdc[drvreg].index == 0) {
+                                                       disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
+                                                       fdc[drvreg].id_written = false;
+                                                       fdc[drvreg].side = sidereg;
+                                                       fdc[drvreg].side_changed = false;
+                                               }
+                                               if(fdc[drvreg].side != sidereg) {
+                                                       fdc[drvreg].side_changed = true;
+                                               }
+                                               if(fdc[drvreg].side_changed) {
+                                                       // abort write track because disk side is changed
+                                               } else if(datareg == 0xf5) {
+                                                       // write a1h in missing clock
+                                               } else if(datareg == 0xf6) {
+                                                       // write c2h in missing clock
+                                               } else if(datareg == 0xf7) {
+                                                       // write crc
+                                                       if(!fdc[drvreg].id_written) {
+                                                               // insert new sector with data crc error
+write_id:
+                                                               uint8_t c = 0, h = 0, r = 0, n = 0;
+                                                               fdc[drvreg].id_written = true;
+                                                               fdc[drvreg].sector_found = false;
+                                                               if(fdc[drvreg].index >= 4) {
+                                                                       c = disk[drvreg]->track[fdc[drvreg].index - 4];
+                                                                       h = disk[drvreg]->track[fdc[drvreg].index - 3];
+                                                                       r = disk[drvreg]->track[fdc[drvreg].index - 2];
+                                                                       n = disk[drvreg]->track[fdc[drvreg].index - 1];
+                                                               }
+                                                               fdc[drvreg].sector_length = 0x80 << (n & 3);
+                                                               fdc[drvreg].sector_index = 0;
+                                                               disk[drvreg]->insert_sector(c, h, r, n, false, true, 0xe5, fdc[drvreg].sector_length);
+                                                       } else if(fdc[drvreg].sector_found) {
+                                                               // clear data crc error if all sector data are written
+                                                               if(fdc[drvreg].sector_index == fdc[drvreg].sector_length) {
+                                                                       disk[drvreg]->set_data_crc_error(false);
+                                                               }
+                                                               fdc[drvreg].id_written = false;
+                                                       } else {
+                                                               // data mark of current sector is not written
+                                                               disk[drvreg]->set_data_mark_missing();
+                                                               goto write_id;
+                                                       }
+                                               } else if(fdc[drvreg].id_written) {
+                                                       if(fdc[drvreg].sector_found) {
+                                                               // sector data
+                                                               if(fdc[drvreg].sector_index < fdc[drvreg].sector_length) {
+                                                                       disk[drvreg]->sector[fdc[drvreg].sector_index] = datareg;
+                                                               }
+                                                               fdc[drvreg].sector_index++;
+                                                       } else if(datareg == 0xf8 || datareg == 0xfb) {
+                                                               // data mark
+                                                               disk[drvreg]->set_deleted(datareg == 0xf8);
+                                                               fdc[drvreg].sector_found = true;
+                                                       }
+                                               }
+                                               disk[drvreg]->track[fdc[drvreg].index] = datareg;
+                                       } else {
+                                               status |= FDC_ST_WRITEFAULT;
+                                               status &= ~FDC_ST_BUSY;
+                                               status &= ~FDC_ST_DRQ;
+                                               cmdtype = 0;
+                                               set_irq(true);
+                                       }
+                                       //fdc[drvreg].index++;
+                               }
+                               if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
+                                       if(!disk[drvreg]->write_protected) {
+                                               if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
+                                                       // data mark of last sector is not written
+                                                       disk[drvreg]->set_data_mark_missing();
+                                               }
+                                               disk[drvreg]->sync_buffer();
+                                       }
+                                       status &= ~FDC_ST_BUSY;
+                                       cmdtype = 0;
+                                       set_irq(true);
+                               } else if(status & FDC_ST_DRQ) {
+                                       if(fdc[drvreg].index == 0) {
+                                               register_drq_event(fdc[drvreg].bytes_before_2nd_drq);
+                                       } else {
+                                               register_drq_event(1);
+                                       }
+                                       if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
+                               }
+                               status &= ~FDC_ST_DRQ;
+                       }
+                       if(!(status & FDC_ST_DRQ)) {
+                               cancel_my_event(EVENT_LOST);
+                               set_drq(false);
+                               fdc[drvreg].access = true;
+                       }
+               }
+               break;
+       }
+}
+
+uint32_t MB8877::read_io8(uint32_t addr)
+{
+       uint32_t val;
+       bool not_ready;
+       bool ready;
+       
+       switch(addr & 3) {
+       case 0:
+               // status reg
+               if(now_search) {
+                       // now sector search
+                       val = FDC_ST_BUSY;
+               } else {
+                       // disk not inserted, motor stop
+                       not_ready = !disk[drvreg]->inserted;
+//#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
+                       if(type_fm7){
+                               if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
+                                       if(!motor_on) not_ready = true;
+                               }
+                       } else
+//#endif
+                       {
+                               if(!motor_on) not_ready = true;
+                       }
+//                     if(!disk[drvreg]->inserted || !motor_on) {
+                       if(not_ready) {
+                               status |= FDC_ST_NOTREADY;
+                       } else {
+                               status &= ~FDC_ST_NOTREADY;
+                       }
+                       // write protected
+                       if(cmdtype == FDC_CMD_TYPE1 || cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
+                               if(disk[drvreg]->inserted && disk[drvreg]->write_protected) {
+                                       status |= FDC_ST_WRITEP;
+                               } else {
+                                       status &= ~FDC_ST_WRITEP;
+                               }
+                       } else {
+                               status &= ~FDC_ST_WRITEP;
+                       }
+                       // track0, index hole
+                       if(cmdtype == FDC_CMD_TYPE1) {
+                               if(fdc[drvreg].track == 0) {
+                                       status |= FDC_ST_TRACK00;
+                               } else {
+                                       status &= ~FDC_ST_TRACK00;
+                               }
+                               // index hole signal width is 5msec (thanks Mr.Sato)
+                               if(!(status & FDC_ST_NOTREADY) && get_cur_position() < disk[drvreg]->get_bytes_per_usec(5000)) {
+                                       status |= FDC_ST_INDEX;
+                               } else {
+                                       status &= ~FDC_ST_INDEX;
+                               }
+                       }
+                       // show busy a moment
+                       val = status;
+                       if(cmdtype == FDC_CMD_TYPE1 && !now_seek) {
+                               status &= ~FDC_ST_BUSY;
+//#ifdef MB8877_NO_BUSY_AFTER_SEEK
+                               if(mb8877_no_busy_after_seek) {
+                                       //#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
+                                       if(type_fm7) {
+                                               if(disk[0]->is_special_disk != SPECIAL_DISK_FM7_XANADU2_D) {
+                                                       val &= ~FDC_ST_BUSY;
+                                               }
+                                       } else
+                                               //#endif
+                                       {
+                                               val &= ~FDC_ST_BUSY;
+                                       }
+                               }
+//#endif
+                       }
+               }
+               if(cmdtype == 0 && !(status & FDC_ST_NOTREADY)) {
+                       // MZ-2000 HuBASIC invites NOT READY status
+                       if(++no_command == 16) {
+                               val |= FDC_ST_NOTREADY;
+                       }
+               } else {
+                       no_command = 0;
+               }
+               // reset irq/drq
+               if(!(status & FDC_ST_DRQ)) {
+                       set_drq(false);
+               }
+               if(!(status & FDC_ST_BUSY)) {
+                       set_irq(false);
+               }
+//#ifdef _FDC_DEBUG_LOG
+               if(fdc_debug_log) this->out_debug_log(_T("FDC\tSTATUS=%2x\n"), val);
+//#endif
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+                       if(invert_registers) {
+                               return (~val) & 0xff;
+//#else
+                       } else {
+                               return val;
+                       }
+//#endif
+       case 1:
+               // track reg
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+                       if(invert_registers) {
+                               return (~trkreg) & 0xff;
+                       } else {
+//#else
+                               return trkreg;
+                       }
+//#endif
+       case 2:
+               // sector reg
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+                       if(invert_registers) {
+                               return (~secreg) & 0xff;
+                       } else {
+//#else
+                               return secreg;
+                       }
+//#endif
+       case 3:
+               // data reg
+               ready = ((status & FDC_ST_DRQ) && !now_search);
+//#if defined(_FM7) || defined(_FM8) || defined(_FM77_VARIANTS) || defined(_FM77AV_VARIANTS)
+               if(type_fm7) {
+                       if(disk[drvreg]->is_special_disk != SPECIAL_DISK_FM7_RIGLAS) {
+                               if(!motor_on) ready = false;
+                       }
+               } else
+//#endif
+               {
+                       if(!motor_on) ready = false;
+               }
+//             if(motor_on && (status & FDC_ST_DRQ) && !now_search) {
+               if(ready) {
+                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC) {
+                               // read or multisector read
+                               if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
+                                       datareg = disk[drvreg]->sector[fdc[drvreg].index];
+                               }
+                               if((fdc[drvreg].index + 1) >= disk[drvreg]->sector_size.sd) {  // Reading complete this sector.
+
+                                       if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
+                                               // data crc error
+//#ifdef _FDC_DEBUG_LOG
+                                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (DATA CRC ERROR)\n"));
+//#endif
+                                               status |= FDC_ST_CRCERR;
+                                               status &= ~FDC_ST_BUSY;
+                                               cmdtype = 0;
+                                               set_irq(true);
+                                       } else if(cmdtype == FDC_CMD_RD_SEC) {
+                                               // single sector
+//#ifdef _FDC_DEBUG_LOG
+                                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR\n"));
+//#endif
+                                               status &= ~FDC_ST_BUSY;
+                                               cmdtype = 0;
+                                               set_irq(true);
+                                       } else {
+                                               // multisector
+//#ifdef _FDC_DEBUG_LOG
+                                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF SECTOR (SEARCH NEXT)\n"));
+//#endif
+                                               register_my_event(EVENT_MULTI1, 30);
+                                               register_my_event(EVENT_MULTI2, 60);
+                                       }
+                               } else { // Still data remain.
+                                       register_drq_event(1);
+                                       if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
+                               }
+                               status &= ~FDC_ST_DRQ;
+                       } else if(cmdtype == FDC_CMD_RD_ADDR) {
+                               // read address
+                               if(fdc[drvreg].index < 6) {
+                                       datareg = disk[drvreg]->id[fdc[drvreg].index];
+                                       //fdc[drvreg].index++;
+                               }
+                               if((fdc[drvreg].index + 1) >= 6) {
+                                       if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
+                                               // id crc error
+                                               status |= FDC_ST_CRCERR;
+                                       }
+                                       status &= ~FDC_ST_BUSY;
+                                       cmdtype = 0;
+                                       set_irq(true);
+//#ifdef _FDC_DEBUG_LOG
+                                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF ID FIELD\n"));
+//#endif
+                               } else {
+                                       register_drq_event(1);
+                                       if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
+                               }
+                               status &= ~FDC_ST_DRQ;
+                       } else if(cmdtype == FDC_CMD_RD_TRK) {
+                               // read track
+                               if(fdc[drvreg].index < disk[drvreg]->get_track_size()) {
+                                       datareg = disk[drvreg]->track[fdc[drvreg].index];
+                                       //fdc[drvreg].index++;
+                               }
+                               if((fdc[drvreg].index + 1) >= disk[drvreg]->get_track_size()) {
+//#ifdef _FDC_DEBUG_LOG
+                                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tEND OF TRACK\n"));
+///#endif
+                                       status &= ~FDC_ST_BUSY;
+                                       status |= FDC_ST_LOSTDATA;
+                                       cmdtype = 0;
+                                       set_irq(true);
+                               } else {
+                                       register_drq_event(1);
+                                       if(fdc[drvreg].count_immediate) fdc[drvreg].index++;
+                               }
+                               status &= ~FDC_ST_DRQ;
+                       }
+                       if(!(status & FDC_ST_DRQ)) {
+                               cancel_my_event(EVENT_LOST);
+                               set_drq(false);
+                               fdc[drvreg].access = true;
+                       }
+               }
+//#ifdef _FDC_DEBUG_LOG
+               if(fdc_debug_log) this->force_out_debug_log(_T("FDC\tDATA=%2x\n"), datareg); // emu->force_out_debug_log()
+//#endif
+//#if defined(HAS_MB8866) || defined(HAS_MB8876)
+               if(invert_registers) {
+                       return (~datareg) & 0xff;
+               } else {
+//#else
+                       return datareg;
+               }
+//#endif
+       }
+       return 0xff;
+}
+
+void MB8877::write_dma_io8(uint32_t addr, uint32_t data)
+{
+       write_io8(3, data);
+}
+
+uint32_t MB8877::read_dma_io8(uint32_t addr)
+{
+       return read_io8(3);
+}
+
+void MB8877::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_MB8877_DRIVEREG) {
+               drvreg = data & _drive_mask;
+               drive_sel = true;
+               seekend_clock = get_current_clock();
+       } else if(id == SIG_MB8877_SIDEREG) {
+               sidereg = (data & mask) ? 1 : 0;
+       } else if(id == SIG_MB8877_MOTOR) {
+               motor_on = ((data & mask) != 0);
+       } 
+}
+
+uint32_t MB8877::read_signal(int ch)
+{
+       if(ch == SIG_MB8877_DRIVEREG) {
+               return drvreg & _drive_mask;
+       } else if(ch == SIG_MB8877_SIDEREG) {
+               return sidereg & 1;
+       } else if(ch == SIG_MB8877_MOTOR) {
+               return motor_on ? 1 : 0;
+       }
+       
+       // get access status
+       uint32_t stat = 0;
+       for(int i = 0; i < _max_drive; i++) {
+               if(fdc[i].access) {
+                       stat |= 1 << i;
+               }
+               fdc[i].access = false;
+       }
+       if(now_search) {
+               stat |= 1 << drvreg;
+       }
+       return stat;
+}
+
+void MB8877::event_callback(int event_id, int err)
+{
+       int event = event_id >> 8;
+       int cmd = event_id & 0xff;
+       //bool need_after_irq = false;
+       register_id[event] = -1;
+       
+       // cancel event if the command is finished or other command is executed
+       if(cmd != cmdtype) {
+               if(event == EVENT_SEEK || event == EVENT_SEEKEND_VERIFY) {
+                       now_seek = false;
+               } else if(event == EVENT_SEARCH) {
+                       now_search = false;
+               }
+               return;
+       }
+       
+       switch(event) {
+       case EVENT_SEEK:
+//#ifdef _FDC_DEBUG_LOG
+               //this->out_debug_log(_T("FDC\tSEEK START\n"));
+//#endif
+               if(seektrk > fdc[drvreg].track) {
+                       fdc[drvreg].track++;
+                       if(d_noise_seek != NULL) d_noise_seek->play();
+                       //need_after_irq = true;
+               } else if(seektrk < fdc[drvreg].track) {
+                       fdc[drvreg].track--;
+                       if(d_noise_seek != NULL) d_noise_seek->play();
+                       //need_after_irq = true;
+               }
+               if((cmdreg & 0x10) || ((cmdreg & 0xf0) == 0)) {
+                       trkreg = fdc[drvreg].track;
+               }
+               if(seektrk != fdc[drvreg].track) {
+                       //if(need_after_irq) {
+                               //set_drq(false);
+                               //set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
+                       //}
+                       register_seek_event();
+                       break;
+               }
+               seekend_clock = get_current_clock();
+//#ifdef HAS_MB89311
+               if(type_mb89311) {
+                       if(extended_mode) {
+                               if((cmdreg & 0xf4) == 0x44) {
+                                       // read-after-seek
+                                       cmd_readdata(true);
+                                       break;
+                               } else if((cmdreg & 0xf4) == 0x64) {
+                                       // write-after-seek
+                                       cmd_writedata(true);
+                                       break;
+                               }
+                       }
+               }
+//#endif
+               status_tmp = status;
+               if(cmdreg & 4) {
+                       // verify
+                       status_tmp |= search_track();
+                       double time;
+                       if(status_tmp & FDC_ST_SEEKERR) {
+                               time = get_usec_to_detect_index_hole(5, true);
+                       } else {
+                               time = get_usec_to_next_trans_pos(true);
+                       }
+                       // 20180128 K.O If seek completed, delay to make IRQ/DRQ at SEEKEND_VERIFY.
+                       register_my_event(EVENT_SEEKEND_VERIFY, time);
+                       break;
+               }
+//     case EVENT_SEEKEND: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
+               now_seek = false;
+               status = status_tmp;
+               status &= (uint8_t)(~FDC_ST_BUSY);      // 20180118 K.O Turn off BUSY flag.
+               set_drq(false);
+               set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
+               break;
+       case EVENT_SEEKEND_VERIFY: // Separate SEEK and SEEKEND when verifying. 20180128 K.O
+               now_seek = false;
+               status &= (uint8_t)(~FDC_ST_BUSY);      // 20180118 K.O Turn off BUSY flag.
+               set_drq(false);
+               set_irq(true);  // 20180118 K.O Turn ON IRQ -> Turn OFF DRQ
+//#ifdef _FDC_DEBUG_LOG
+               //this->out_debug_log(_T("FDC\tSEEK END\n"));
+//#endif
+               break;
+       case EVENT_SEARCH:
+               now_search = false;
+               if(status_tmp & FDC_ST_RECNFND) {
+//#if defined(_X1) || defined(_X1TWIN) || defined(_X1TURBO) || defined(_X1TURBOZ)
+                       if(type_x1) {
+                               // for SHARP X1 Batten Tanuki
+                               if(disk[drvreg]->is_special_disk == SPECIAL_DISK_X1_BATTEN && drive_sel) {
+                                       status_tmp &= ~FDC_ST_RECNFND;
+                               }
+                       }
+//#endif
+//#ifdef _FDC_DEBUG_LOG
+                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH NG\n"));
+//#endif
+                       status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
+                       cmdtype = 0;
+                       set_drq(false);
+                       set_irq(true);
+               } else if(status_tmp & FDC_ST_WRITEFAULT) {
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tWRITE PROTECTED\n"));
+//#endif
+                       status = status_tmp & ~(FDC_ST_BUSY | FDC_ST_DRQ);
+                       cmdtype = 0;
+                       set_drq(false);
+                       set_irq(true);
+               } else {
+                       status = status_tmp | (FDC_ST_BUSY | FDC_ST_DRQ);
+                       if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                               register_lost_event(8);
+                       } else if(cmdtype == FDC_CMD_WR_TRK) {
+                               register_lost_event(3);
+                       } else {
+                               register_lost_event(1);
+                       }
+                       fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
+                       fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
+                       set_drq(true);
+                       drive_sel = false;
+                       this->out_debug_log("DRQ ON@SEARCH: %d\n", prev_drq_clock);
+//#ifdef _FDC_DEBUG_LOG
+                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tSEARCH OK\n"));
+//#endif
+               }
+               break;
+       case EVENT_DRQ:
+               if(status & FDC_ST_BUSY) {
+                       status |= FDC_ST_DRQ;
+                       register_lost_event(1);
+                       if((cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) && fdc[drvreg].index == 0) {
+                               fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_drq) % disk[drvreg]->get_track_size();
+                       } else {
+                               fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
+                       }
+                       if(cmdtype == FDC_CMD_RD_SEC || cmdtype == FDC_CMD_RD_MSEC ||
+                          cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC ||
+                          cmdtype == FDC_CMD_RD_TRK || cmdtype == FDC_CMD_WR_TRK  ||
+                          cmdtype == FDC_CMD_RD_ADDR) {
+                               if(!(fdc[drvreg].count_immediate)) fdc[drvreg].index++;
+                       }
+                       fdc[drvreg].prev_clock = prev_drq_clock = get_current_clock();
+
+                       set_drq(true);
+                       this->out_debug_log("DRQ ON@DRQ: %d\n", prev_drq_clock);
+//#ifdef _FDC_DEBUG_LOG
+                       //this->out_debug_log(_T("FDC\tDRQ!\n"));
+//#endif
+               }
+               break;
+       case EVENT_MULTI1:
+               secreg++;
+               break;
+       case EVENT_MULTI2:
+               if(cmdtype == FDC_CMD_RD_MSEC) {
+                       cmd_readdata(false);
+               } else if(cmdtype == FDC_CMD_WR_MSEC) {
+                       cmd_writedata(false);
+               }
+               break;
+       case EVENT_LOST:
+               if(status & FDC_ST_BUSY) {
+//#ifdef _FDC_DEBUG_LOG
+                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tDATA LOST\n"));
+//#endif
+                       if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC || cmdtype == FDC_CMD_WR_TRK) {
+                               if(fdc[drvreg].index == 0) { // HEAD of REGION
+                                       cmdtype = 0;
+                                       //if((status & FDC_ST_DRQ) != 0) { // 20180130 FORCE DOWN DRQ ON LOST-DATA.
+                                               status |= FDC_ST_LOSTDATA;
+                                               status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
+                                               set_drq(false);
+                                               //}
+                                       set_irq(true);
+                               } else { // STILL WRITING
+                                       write_io8(3, 0x00);
+                                       //if((status & FDC_ST_DRQ) != 0) {
+                                               status |= FDC_ST_LOSTDATA;
+                                               status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
+                                               set_irq(true);
+                                               set_drq(false);
+                                               //cmdtype = 0;
+                                               //}
+                               }
+                       } else { // READ 
+                               //if((status & FDC_ST_DRQ) != 0) {
+                                       status |= FDC_ST_LOSTDATA;
+                                       status &= (uint8_t)(~(FDC_ST_DRQ | FDC_ST_BUSY));
+                                       set_irq(true);
+                                       set_drq(false);
+                                       //}
+                               read_io8(3);
+                       }
+                       status |= FDC_ST_LOSTDATA;
+               }
+               break;
+       }
+}
+
+// ----------------------------------------------------------------------------
+// command
+// ----------------------------------------------------------------------------
+
+void MB8877::process_cmd()
+{
+       set_irq(false);
+       set_drq(false);
+       
+//#ifdef HAS_MB89311
+       // MB89311 mode commands
+       if(type_mb89311) {
+                               if(cmdreg == 0xfc) {
+                               // delay (may be only in extended mode)
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (DELAY   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmdtype = status = 0;
+                               return;
+                       } else if(cmdreg == 0xfd) {
+                               // assign parameter
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN PAR) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmdtype = status = 0;
+                               return;
+                       } else if(cmdreg == 0xfe) {
+                               // assign mode
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (ASGN MOD) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               extended_mode = !extended_mode;
+                               cmdtype = status = 0;
+                               return;
+                       } else if(cmdreg == 0xff) {
+                               // reset (may be only in extended mode)
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (RESET   ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmd_forceint();
+                               extended_mode = true;
+                               return;
+                       } else if(extended_mode) {
+                               // type-1
+                               if((cmdreg & 0xeb) == 0x21) {
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (STEP IN ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmd_stepin();
+                               return;
+                       } else if((cmdreg & 0xeb) == 0x22) {
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (STEP OUT) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmd_stepout();
+                               return;
+                               // type-2
+                       } else if((cmdreg & 0xf4) == 0x44) {
+                               // read-after-seek
+//#ifdef _FDC_DEBUG_LOG
+                                       if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (RDaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                                       cmd_seek();
+                                       return;
+                               } else if((cmdreg & 0xf4) == 0x64) {
+                                       // write-after-seek
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (WRaftSEK) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmd_seek();
+                       return;
+                       // type-3
+                       } else if((cmdreg & 0xfb) == 0xf1) {
+                                       // format
+//#ifdef _FDC_DEBUG_LOG
+                               if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (FORMAT  ) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+                               cmd_format();
+                               return;
+                               }
+                               }
+       }
+//#endif
+       
+       // MB8877 mode commands
+//#ifdef _FDC_DEBUG_LOG
+       static const _TCHAR *cmdstr[0x10] = {
+               _T("RESTORE "), _T("SEEK    "), _T("STEP    "), _T("STEP    "),
+               _T("STEP IN "), _T("STEP IN "), _T("STEP OUT"), _T("STEP OUT"),
+               _T("RD DATA "), _T("RD DATA "), _T("RD DATA "), _T("WR DATA "),
+               _T("RD ADDR "), _T("FORCEINT"), _T("RD TRACK"), _T("WR TRACK")
+       };
+       if(fdc_debug_log) this->out_debug_log(_T("FDC\tCMD=%2xh (%s) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, cmdstr[cmdreg >> 4], datareg, drvreg, trkreg, sidereg, secreg);
+//#endif
+       
+       switch(cmdreg & 0xf8) {
+       // type-1
+       case 0x00: case 0x08:
+               cmd_restore();
+               update_head_flag(drvreg, (cmdreg & 8) != 0);
+               break;
+       case 0x10: case 0x18:
+               cmd_seek();
+               update_head_flag(drvreg, (cmdreg & 8) != 0);
+               break;
+       case 0x20: case 0x28:
+       case 0x30: case 0x38:
+               cmd_step();
+               update_head_flag(drvreg, (cmdreg & 8) != 0);
+               break;
+       case 0x40: case 0x48:
+       case 0x50: case 0x58:
+               cmd_stepin();
+               update_head_flag(drvreg, (cmdreg & 8) != 0);
+               break;
+       case 0x60: case 0x68:
+       case 0x70: case 0x78:
+               cmd_stepout();
+               update_head_flag(drvreg, (cmdreg & 8) != 0);
+               break;
+       // type-2
+       case 0x80: case 0x88:
+       case 0x90: case 0x98:
+               cmd_readdata(true);
+               update_head_flag(drvreg, true);
+               break;
+       case 0xa0:case 0xa8:
+       case 0xb0: case 0xb8:
+               cmd_writedata(true);
+               update_head_flag(drvreg, true);
+               break;
+       // type-3
+       case 0xc0:
+               cmd_readaddr();
+               update_head_flag(drvreg, true);
+               break;
+       case 0xe0:
+               cmd_readtrack();
+               update_head_flag(drvreg, true);
+               break;
+       case 0xf0:
+               cmd_writetrack();
+               update_head_flag(drvreg, true);
+               break;
+       // type-4
+       case 0xd0: case 0xd8:
+               cmd_forceint();
+               break;
+       // unknown command
+       default:
+               break;
+       }
+}
+
+void MB8877::cmd_restore()
+{
+       // type-1 restore
+       cmdtype = FDC_CMD_TYPE1;
+       if(!check_drive()) {
+               return;
+       }
+       if((cmdreg & 0x08) != 0) { // Head engage
+               status = FDC_ST_HEADENG | FDC_ST_BUSY;
+       } else {
+               status = FDC_ST_BUSY;
+       }
+       set_irq(false);
+       set_drq(false);
+
+       if(fdc[drvreg].track < 0) {
+               fdc[drvreg].track = 0;
+               trkreg = 0;
+       } else if(fdc[drvreg].track >= 80) {
+               fdc[drvreg].track = 80;
+               trkreg = 80;
+       } else {
+               trkreg = fdc[drvreg].track;
+       }
+       datareg = 0;
+       
+       seektrk = 0;
+       seekvct = true;
+       
+       register_seek_event();
+}
+
+void MB8877::cmd_seek()
+{
+       // type-1 seek
+       cmdtype = FDC_CMD_TYPE1;
+       if(!check_drive()) {
+               return;
+       }
+       if((cmdreg & 0x08) != 0) { // Head engage
+               status = FDC_ST_HEADENG | FDC_ST_BUSY;
+       } else {
+               status = FDC_ST_BUSY;
+       }               
+       
+//     seektrk = (uint8_t)(fdc[drvreg].track + datareg - trkreg);
+       seektrk = datareg;
+//#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
+                       seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+               } else {
+                       seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+               }
+       } else  if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
+               seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+       } else {
+               seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+       }               
+       seekvct = !(seektrk > fdc[drvreg].track);
+       
+       if(cmdreg & 4) {
+               // verify
+               if(trkreg != fdc[drvreg].track) {
+                       status |= FDC_ST_SEEKERR;
+                       trkreg = fdc[drvreg].track;
+               }
+       }
+       register_seek_event();
+}
+
+void MB8877::cmd_step()
+{
+       // type-1 step
+       if(seekvct) {
+               cmd_stepout();
+       } else {
+               cmd_stepin();
+       }
+}
+
+void MB8877::cmd_stepin()
+{
+       // type-1 step in
+       cmdtype = FDC_CMD_TYPE1;
+       if(!check_drive()) {
+               return;
+       }
+       if((cmdreg & 0x08) != 0) { // Head engage
+               status = FDC_ST_HEADENG | FDC_ST_BUSY;
+       } else {
+               status = FDC_ST_BUSY;
+       }               
+       seektrk = fdc[drvreg].track + 1;
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
+                       seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+               } else {
+                       seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+               }
+       } else  if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
+               seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+       } else {
+               seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+       }               
+       seekvct = false;
+       
+       if(cmdreg & 4) {
+               // verify
+               if(trkreg != fdc[drvreg].track) {
+                       status |= FDC_ST_SEEKERR;
+//                     trkreg = fdc[drvreg].track;
+               }
+       }
+       register_seek_event();
+}
+
+void MB8877::cmd_stepout()
+{
+       // type-1 step out
+       cmdtype = FDC_CMD_TYPE1;
+       if(!check_drive()) {
+               return;
+       }
+       if((cmdreg & 0x08) != 0) { // Head engage
+               status = FDC_ST_HEADENG | FDC_ST_BUSY;
+       } else {
+               status = FDC_ST_BUSY;
+       }               
+       
+       seektrk = fdc[drvreg].track - 1;
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->drive_type != DRIVE_TYPE_2D) {
+                       seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+               } else {
+                       seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+               }
+       } else  if(disk[drvreg]->media_type != MEDIA_TYPE_2D){
+               seektrk = (seektrk > 83) ? 83 : (seektrk < 0) ? 0 : seektrk;
+       } else {
+               seektrk = (seektrk > 41) ? 41 : (seektrk < 0) ? 0 : seektrk;
+       }               
+       seekvct = true;
+       
+       if(cmdreg & 4) {
+               // verify
+               if(trkreg != fdc[drvreg].track) {
+                       status |= FDC_ST_SEEKERR;
+//                     trkreg = fdc[drvreg].track;
+               }
+       }
+       register_seek_event();
+}
+
+void MB8877::cmd_readdata(bool first_sector)
+{
+       // type-2 read data
+       cmdtype = (cmdreg & 0x10) ? FDC_CMD_RD_MSEC : FDC_CMD_RD_SEC;
+       if(!check_drive2()) {
+               return;
+       }
+       status = FDC_ST_BUSY;
+       status_tmp = search_sector();
+       now_search = true;
+       
+       double time;
+       if(status_tmp & FDC_ST_RECNFND) {
+               time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
+       } else {
+               time = get_usec_to_start_trans(first_sector);
+       }
+       register_my_event(EVENT_SEARCH, time);
+       cancel_my_event(EVENT_LOST);
+}
+
+void MB8877::cmd_writedata(bool first_sector)
+{
+       // type-2 write data
+       cmdtype = (cmdreg & 0x10) ? FDC_CMD_WR_MSEC : FDC_CMD_WR_SEC;
+       if(!check_drive2()) {
+               return;
+       }
+       status = FDC_ST_BUSY;
+       status_tmp = search_sector() & ~FDC_ST_RECTYPE;
+       now_search = true;
+       sector_changed = false;
+       
+       double time;
+       if(status_tmp & FDC_ST_RECNFND) {
+               time = get_usec_to_detect_index_hole(5, first_sector && ((cmdreg & 4) != 0));
+       } else if(status & FDC_ST_WRITEFAULT) {
+               time = (cmdreg & 4) ? DELAY_TIME : 1;
+       } else {
+               time = get_usec_to_start_trans(first_sector);
+       }
+       register_my_event(EVENT_SEARCH, time);
+       cancel_my_event(EVENT_LOST);
+}
+
+void MB8877::cmd_readaddr()
+{
+       // type-3 read address
+       cmdtype = FDC_CMD_RD_ADDR;
+       if(!check_drive2()) {
+               return;
+       }
+       status = FDC_ST_BUSY;
+       status_tmp = search_addr();
+       now_search = true;
+       
+       double time;
+       if(status_tmp & FDC_ST_RECNFND) {
+               time = get_usec_to_detect_index_hole(5, ((cmdreg & 4) != 0));
+       } else {
+               time = get_usec_to_start_trans(true);
+       }
+       register_my_event(EVENT_SEARCH, time);
+       cancel_my_event(EVENT_LOST);
+}
+
+void MB8877::cmd_readtrack()
+{
+       // type-3 read track
+       cmdtype = FDC_CMD_RD_TRK;
+       if(!check_drive2()) {
+               return;
+       }
+       status = FDC_ST_BUSY;
+       status_tmp = 0;
+       
+       disk[drvreg]->make_track(fdc[drvreg].track, sidereg);
+       fdc[drvreg].index = 0;
+       now_search = true;
+       
+       fdc[drvreg].next_trans_position = 1;
+       double time = get_usec_to_detect_index_hole(1, ((cmdreg & 4) != 0));
+       register_my_event(EVENT_SEARCH, time);
+       cancel_my_event(EVENT_LOST);
+}
+
+void MB8877::cmd_writetrack()
+{
+       // type-3 write track
+       cmdtype = FDC_CMD_WR_TRK;
+       if(!check_drive2()) {
+               return;
+       }
+       status = FDC_ST_BUSY;
+       status_tmp = 0;
+       
+       fdc[drvreg].index = 0;
+       fdc[drvreg].id_written = false;
+       now_search = true;
+       
+       double time;
+       if(disk[drvreg]->write_protected) {
+               status_tmp = FDC_ST_WRITEFAULT;
+               time = (cmdreg & 4) ? DELAY_TIME : 1;
+       } else {
+               if(cmdreg & 4) {
+                       // wait 15msec before raise first drq
+                       fdc[drvreg].next_trans_position = (get_cur_position() + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
+                       time = DELAY_TIME;
+               } else {
+                       // raise first drq soon
+                       fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
+                       time = disk[drvreg]->get_usec_per_bytes(1);
+               }
+               // wait at least 3bytes before check index hole and raise second drq
+               fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->get_track_size() - fdc[drvreg].next_trans_position;
+               if(fdc[drvreg].bytes_before_2nd_drq < 3) {
+                       fdc[drvreg].bytes_before_2nd_drq += disk[drvreg]->get_track_size();
+               }
+       }
+       register_my_event(EVENT_SEARCH, time);
+       cancel_my_event(EVENT_LOST);
+}
+
+//#ifdef HAS_MB89311
+void MB8877::cmd_format()
+{
+       if(type_mb89311) {
+               // type-3 format (FIXME: need to implement)
+               cmdtype = FDC_CMD_WR_TRK;
+               status = FDC_ST_BUSY;
+               status_tmp = 0;
+               
+               fdc[drvreg].index = 0;
+               fdc[drvreg].id_written = false;
+               now_search = true;
+               
+               status_tmp = FDC_ST_WRITEFAULT;
+               double time = (cmdreg & 4) ? DELAY_TIME : 1;
+               
+               register_my_event(EVENT_SEARCH, time);
+               cancel_my_event(EVENT_LOST);
+       }
+}
+//#endif
+
+void MB8877::cmd_forceint()
+{
+       // type-4 force interrupt
+       if(cmdtype == FDC_CMD_TYPE1) {
+               // abort restore/seek/step command
+               if(now_seek) {
+                       if(seektrk > fdc[drvreg].track) {
+                               fdc[drvreg].track++;
+                       } else if(seektrk < fdc[drvreg].track) {
+                               fdc[drvreg].track--;
+                       }
+                       if((cmdreg_tmp & 0x10) || ((cmdreg_tmp & 0xf0) == 0)) {
+                               trkreg = fdc[drvreg].track;
+                       }
+               }
+       } else if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+               // abort write sector command
+               if(sector_changed) {
+                       disk[drvreg]->set_data_crc_error(false);
+               }
+       } else if(cmdtype == FDC_CMD_WR_TRK) {
+               // abort write track command
+               if(!disk[drvreg]->write_protected) {
+                       if(fdc[drvreg].id_written && !fdc[drvreg].sector_found) {
+                               // data mark of last sector is not written
+                               disk[drvreg]->set_data_mark_missing();
+                       }
+                       disk[drvreg]->sync_buffer();
+               }
+       }
+       now_search = now_seek = sector_changed = false;
+       
+//#ifdef HAS_MB89311
+       if((cmdreg == 0xff) && (type_mb89311)) {
+               // reset command
+               cmdtype = FDC_CMD_TYPE1;
+               status = FDC_ST_HEADENG;
+       } else {
+//#endif
+               if(cmdtype == 0 || !(status & FDC_ST_BUSY)) {
+                       cmdtype = FDC_CMD_TYPE1;
+                   if(!type_fm7) status = FDC_ST_HEADENG; // Hack for FUKUALL.d77 .
+               }
+               status &= ~FDC_ST_BUSY;
+               
+               // force interrupt if bit0-bit3 is high
+               if(cmdreg & 0x0f) {
+                       set_irq(true);
+               }
+//#ifdef HAS_MB89311
+       }
+//#endif
+       
+       cancel_my_event(EVENT_SEEK);
+       cancel_my_event(EVENT_SEEKEND_VERIFY);
+       cancel_my_event(EVENT_SEARCH);
+       cancel_my_event(EVENT_DRQ);
+       cancel_my_event(EVENT_MULTI1);
+       cancel_my_event(EVENT_MULTI2);
+       cancel_my_event(EVENT_LOST);
+}
+
+void MB8877::update_head_flag(int drv, bool head_load)
+{
+       if(fdc[drv].head_load != head_load) {
+               if(head_load) {
+                       if(d_noise_head_down != NULL) d_noise_head_down->play();
+               } else {
+                       if(d_noise_head_up != NULL) d_noise_head_up->play();
+               }
+               fdc[drv].head_load = head_load;
+       }
+}
+
+// ----------------------------------------------------------------------------
+// media handler
+// ----------------------------------------------------------------------------
+
+uint8_t MB8877::search_track()
+{
+       // get track
+       int track = fdc[drvreg].track;
+//#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
+                       if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
+                               track >>= 1;
+                       }
+               } else {        // OS-9 2DD Access fix by Ryu Takegami
+                       if((disk[drvreg]->media_type != MEDIA_TYPE_2D) &&
+                          (disk[drvreg]->media_type != MEDIA_TYPE_UNK)) {
+                               if(disk[drvreg]->drive_type == DRIVE_TYPE_2D) {
+                                       track <<= 1;
+                               }
+                       }
+               }
+       }
+//#endif
+       if(!disk[drvreg]->get_track(track, sidereg)){
+               return FDC_ST_SEEKERR;
+       }
+       
+       // verify track number
+       if(disk[drvreg]->ignore_crc()) {
+               for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
+                       disk[drvreg]->get_sector(-1, -1, i);
+                       if(disk[drvreg]->id[0] == trkreg) {
+                               fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
+                               fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
+                               return 0;
+                       }
+               }
+       } else {
+               for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
+                       disk[drvreg]->get_sector(-1, -1, i);
+                       if(disk[drvreg]->id[0] == trkreg && !disk[drvreg]->addr_crc_error) {
+                               fdc[drvreg].next_trans_position = disk[drvreg]->id_position[i] + 4 + 2;
+                               fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[i];
+                               return 0;
+                       }
+               }
+               for(int i = 0; i < disk[drvreg]->sector_num.sd; i++) {
+                       disk[drvreg]->get_sector(-1, -1, i);
+                       if(disk[drvreg]->id[0] == trkreg) {
+                               return FDC_ST_SEEKERR | FDC_ST_CRCERR;
+                       }
+               }
+       }
+       return FDC_ST_SEEKERR;
+}
+
+uint8_t MB8877::search_sector()
+{
+       // write protect
+       if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+               if(disk[drvreg]->write_protected) {
+                       return FDC_ST_WRITEFAULT;
+               }
+       }
+       
+       // get track
+       int track = fdc[drvreg].track;
+//#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
+                       if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
+                               track >>= 1;
+                       }
+               } else {        // OS-9 2DD Access fix by Ryu Takegami
+                       if((disk[drvreg]->media_type != MEDIA_TYPE_2D) &&
+                          (disk[drvreg]->media_type != MEDIA_TYPE_UNK)) {
+                               if(disk[drvreg]->drive_type == DRIVE_TYPE_2D) {
+                                       track <<= 1;
+                               }
+                       }
+               }
+       }
+//#endif
+       if(!disk[drvreg]->get_track(track, sidereg)) {
+               return FDC_ST_RECNFND;
+       }
+       
+       // get current position
+       int sector_num = disk[drvreg]->sector_num.sd;
+       int position = get_cur_position();
+       
+       if(position > disk[drvreg]->am1_position[sector_num - 1]) {
+               position -= disk[drvreg]->get_track_size();
+       }
+       
+       // first scanned sector
+       int first_sector = 0;
+       for(int i = 0; i < sector_num; i++) {
+               if(position < disk[drvreg]->am1_position[i]) {
+                       first_sector = i;
+                       break;
+               }
+       }
+       
+       // scan sectors
+       for(int i = 0; i < sector_num; i++) {
+               // get sector
+               int index = (first_sector + i) % sector_num;
+               disk[drvreg]->get_sector(-1, -1, index);
+               
+               // check id
+               if(disk[drvreg]->id[0] != trkreg) {
+                       continue;
+               }
+//#if !defined(HAS_MB8866)
+               if(!type_mb8866) {
+                       if((cmdreg & 2) && (disk[drvreg]->id[1] & 1) != ((cmdreg >> 3) & 1)) {
+                               continue;
+                       }
+               }
+//#endif
+               if(disk[drvreg]->id[2] != secreg) {
+                       continue;
+               }
+               if(disk[drvreg]->sector_size.sd == 0) {
+                       continue;
+               }
+               if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
+                       // id crc error
+                       disk[drvreg]->sector_size.sd = 0;
+                       return FDC_ST_RECNFND | FDC_ST_CRCERR;
+               }
+               
+               // sector found
+               if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                       fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
+                       fdc[drvreg].bytes_before_2nd_drq = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
+               } else {
+                       fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
+               }
+               fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[index];
+               fdc[drvreg].index = 0;
+//#ifdef _FDC_DEBUG_LOG
+           if(fdc_debug_log) this->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
+                       disk[drvreg]->sector_size.sd,
+                       disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
+                       disk[drvreg]->id[4], disk[drvreg]->id[5],
+                       disk[drvreg]->data_crc_error ? 1 : 0);
+//#endif
+               return (disk[drvreg]->deleted ? FDC_ST_RECTYPE : 0);
+       }
+       
+       // sector not found
+       disk[drvreg]->sector_size.sd = 0;
+       return FDC_ST_RECNFND;
+}
+
+uint8_t MB8877::search_addr()
+{
+       // get track
+       int track = fdc[drvreg].track;
+//#if defined(_FM77AV40) || defined(_FM77AV40EX) || defined(_FM77AV40SX) || defined(_FM77AV20) || defined(_FM77AV20EX)
+       if(type_fm77av_2dd) {
+               if(disk[drvreg]->media_type == MEDIA_TYPE_2D) {
+                       if((disk[drvreg]->drive_type == DRIVE_TYPE_2DD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_2HD) ||
+                          (disk[drvreg]->drive_type == DRIVE_TYPE_144)) {
+                               track >>= 1;
+                       }
+               } else {        // OS-9 2DD Access fix by Ryu Takegami
+                       if((disk[drvreg]->media_type != MEDIA_TYPE_2D) &&
+                          (disk[drvreg]->media_type != MEDIA_TYPE_UNK)) {
+                               if(disk[drvreg]->drive_type == DRIVE_TYPE_2D) {
+                                       track <<= 1;
+                               }
+                       }
+               }
+       }
+//#endif
+       if(!disk[drvreg]->get_track(track, sidereg)) {
+               return FDC_ST_RECNFND;
+       }
+       
+       // get current position
+       int sector_num = disk[drvreg]->sector_num.sd;
+       int position = get_cur_position();
+       
+       if(position > disk[drvreg]->am1_position[sector_num - 1]) {
+               position -= disk[drvreg]->get_track_size();
+       }
+       
+       // first scanned sector
+       int first_sector = 0;
+       for(int i = 0; i < sector_num; i++) {
+               if(position < disk[drvreg]->am1_position[i]) {
+                       first_sector = i;
+                       break;
+               }
+       }
+       
+       // get sector
+       if(disk[drvreg]->get_sector(-1, -1, first_sector)) {
+               fdc[drvreg].next_trans_position = disk[drvreg]->id_position[first_sector] + 1;
+               fdc[drvreg].next_am1_position = disk[drvreg]->am1_position[first_sector];
+               fdc[drvreg].index = 0;
+               secreg = disk[drvreg]->id[0];
+               return 0;
+       }
+       
+       // sector not found
+       disk[drvreg]->sector_size.sd = 0;
+       return FDC_ST_RECNFND;
+}
+
+// ----------------------------------------------------------------------------
+// timing
+// ----------------------------------------------------------------------------
+
+int MB8877::get_cur_position()
+{
+       return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
+}
+
+double MB8877::get_usec_to_start_trans(bool first_sector)
+{
+       // get time from current position
+       double time = get_usec_to_next_trans_pos(first_sector && ((cmdreg & 4) != 0));
+       if(first_sector && time < 60000 - get_passed_usec(seekend_clock)) {
+               time += disk[drvreg]->get_usec_per_track();
+       }
+       return time;
+}
+
+double MB8877::get_usec_to_next_trans_pos(bool delay)
+{
+       int position = get_cur_position();
+       
+       if(disk[drvreg]->invalid_format) {
+               // XXX: this track is invalid format and the calculated sector position may be incorrect.
+               // so use the constant period
+               return 50000;
+       } else if(/*disk[drvreg]->no_skew && */!disk[drvreg]->correct_timing()) {
+               // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
+               // so use the period to search the next sector from the current position
+               int sector_num = disk[drvreg]->sector_num.sd;
+               int bytes = -1;
+               
+               if(position > disk[drvreg]->am1_position[sector_num - 1]) {
+                       position -= disk[drvreg]->get_track_size();
+               }
+               for(int i = 0; i < sector_num; i++) {
+                       if(position < disk[drvreg]->am1_position[i]) {
+                               if(cmdtype == FDC_CMD_WR_SEC || cmdtype == FDC_CMD_WR_MSEC) {
+                                       bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
+                               } else {
+                                       bytes = (disk[drvreg]->data_position[i] + 1) - position;
+                               }
+                               if(bytes < 0) {
+                                       bytes += disk[drvreg]->get_track_size(); // to make sure
+                               }
+                               break;
+                       }
+               }
+               if(bytes > 0) {
+                       return disk[drvreg]->get_usec_per_bytes(bytes);
+               }
+               return 50000;
+       }
+       if(delay) {
+               position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
+       }
+       int bytes = fdc[drvreg].next_trans_position - position;
+       if(fdc[drvreg].next_am1_position < position || bytes < 0) {
+               bytes += disk[drvreg]->get_track_size();
+       }
+       double time = disk[drvreg]->get_usec_per_bytes(bytes);
+       if(delay) {
+               time += DELAY_TIME;
+       }
+       return time;
+}
+
+double MB8877::get_usec_to_detect_index_hole(int count, bool delay)
+{
+       int position = get_cur_position();
+       if(delay) {
+               position = (position + disk[drvreg]->get_bytes_per_usec(DELAY_TIME)) % disk[drvreg]->get_track_size();
+       }
+       int bytes = disk[drvreg]->get_track_size() * count - position;
+       if(bytes < 0) {
+               bytes += disk[drvreg]->get_track_size();
+       }
+       double time = disk[drvreg]->get_usec_per_bytes(bytes);
+       if(delay) {
+               time += DELAY_TIME;
+       }
+       return time;
+}
+
+// ----------------------------------------------------------------------------
+// irq / drq
+// ----------------------------------------------------------------------------
+
+void MB8877::set_irq(bool val)
+{
+       write_signals(&outputs_irq, val ? 0xffffffff : 0);
+}
+
+void MB8877::set_drq(bool val)
+{
+       write_signals(&outputs_drq, val ? 0xffffffff : 0);
+}
+
+// ----------------------------------------------------------------------------
+// user interface
+// ----------------------------------------------------------------------------
+
+void MB8877::open_disk(int drv, const _TCHAR* file_path, int bank)
+{
+       if(drv < _max_drive) {
+               disk[drv]->open(file_path, bank);
+#if defined(_USE_QT)
+               if((disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) || (config.disk_count_immediate[drv])) {
+#else
+               if(disk[drv]->is_special_disk == SPECIAL_DISK_FM7_FLEX) {
+#endif
+                       fdc[drv].count_immediate = true;
+               } else {
+                       fdc[drv].count_immediate = false;
+               }
+       }
+}
+
+void MB8877::close_disk(int drv)
+{
+       if(drv < _max_drive) {
+               disk[drv]->close();
+               cmdtype = 0;
+               update_head_flag(drvreg, false);
+               fdc[drv].count_immediate = false;
+       }
+}
+
+bool MB8877::is_disk_inserted(int drv)
+{
+       if(drv < _max_drive) {
+               return disk[drv]->inserted;
+       }
+       return false;
+}
+
+void MB8877::is_disk_protected(int drv, bool value)
+{
+       if(drv < _max_drive) {
+               disk[drv]->write_protected = value;
+       }
+}
+
+bool MB8877::is_disk_protected(int drv)
+{
+       if(drv < _max_drive) {
+               return disk[drv]->write_protected;
+       }
+       return false;
+}
+
+void MB8877::set_drive_type(int drv, uint8_t type)
+{
+       if(drv < _max_drive) {
+               disk[drv]->drive_type = type;
+       }
+}
+
+uint8_t MB8877::get_drive_type(int drv)
+{
+       if(drv < _max_drive) {
+               return disk[drv]->drive_type;
+       }
+       return DRIVE_TYPE_UNK;
+}
+
+void MB8877::set_drive_rpm(int drv, int rpm)
+{
+       if(drv < _max_drive) {
+               disk[drv]->drive_rpm = rpm;
+       }
+}
+
+void MB8877::set_drive_mfm(int drv, bool mfm)
+{
+       if(drv < _max_drive) {
+               disk[drv]->drive_mfm = mfm;
+       }
+}
+
+void MB8877::set_track_size(int drv, int size)
+{
+       if(drv < _max_drive) {
+               disk[drv]->track_size = size;
+       }
+}
+
+uint8_t MB8877::fdc_status()
+{
+       // for each virtual machines
+//#if defined(_FMR50) || defined(_FMR60)
+       if(type_fmr50 || type_fmr60) {
+               return disk[drvreg]->inserted ? 2 : 0;
+       }
+//#else
+       return 0;
+//#endif
+}
+
+void MB8877::update_config()
+{
+       if(d_noise_seek != NULL) {
+               d_noise_seek->set_mute(!config.sound_noise_fdd);
+       }
+       if(d_noise_head_down != NULL) {
+               d_noise_head_down->set_mute(!config.sound_noise_fdd);
+       }
+       if(d_noise_head_up != NULL) {
+               d_noise_head_up->set_mute(!config.sound_noise_fdd);
+       }
+}
+
+#define STATE_VERSION  6
+
+void MB8877::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->Fwrite(fdc, sizeof(fdc), 1);
+       for(int i = 0; i < _max_drive; i++) {
+               disk[i]->save_state(state_fio);
+       }
+       state_fio->FputUint8(status);
+       state_fio->FputUint8(status_tmp);
+       state_fio->FputUint8(cmdreg);
+       state_fio->FputUint8(cmdreg_tmp);
+       state_fio->FputUint8(trkreg);
+       state_fio->FputUint8(secreg);
+       state_fio->FputUint8(datareg);
+       state_fio->FputUint8(drvreg);
+       state_fio->FputUint8(sidereg);
+       state_fio->FputUint8(cmdtype);
+       state_fio->Fwrite(register_id, sizeof(register_id), 1);
+       state_fio->FputBool(now_search);
+       state_fio->FputBool(now_seek);
+       state_fio->FputBool(sector_changed);
+       state_fio->FputInt32(no_command);
+       state_fio->FputInt32(seektrk);
+       state_fio->FputBool(seekvct);
+       state_fio->FputBool(motor_on);
+       state_fio->FputBool(drive_sel);
+       state_fio->FputUint32(prev_drq_clock);
+       state_fio->FputUint32(seekend_clock);
+}
+
+bool MB8877::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       state_fio->Fread(fdc, sizeof(fdc), 1);
+       for(int i = 0; i < _max_drive; i++) {
+               if(!disk[i]->load_state(state_fio)) {
+                       return false;
+               }
+       }
+       status = state_fio->FgetUint8();
+       status_tmp = state_fio->FgetUint8();
+       cmdreg = state_fio->FgetUint8();
+       cmdreg_tmp = state_fio->FgetUint8();
+       trkreg = state_fio->FgetUint8();
+       secreg = state_fio->FgetUint8();
+       datareg = state_fio->FgetUint8();
+       drvreg = state_fio->FgetUint8();
+       sidereg = state_fio->FgetUint8();
+       cmdtype = state_fio->FgetUint8();
+       state_fio->Fread(register_id, sizeof(register_id), 1);
+       now_search = state_fio->FgetBool();
+       now_seek = state_fio->FgetBool();
+       sector_changed = state_fio->FgetBool();
+       no_command = state_fio->FgetInt32();
+       seektrk = state_fio->FgetInt32();
+       seekvct = state_fio->FgetBool();
+       motor_on = state_fio->FgetBool();
+       drive_sel = state_fio->FgetBool();
+       prev_drq_clock = state_fio->FgetUint32();
+       seekend_clock = state_fio->FgetUint32();
+       return true;
+}
+