OSDN Git Service

[General] Convert sourcecode's CRLF format: DOS(WINDOWS) to Unix, to apply patches...
[csp-qt/common_source_project-fm7.git] / source / src / vm / mz700 / quickdisk.cpp
index f5e906e..e969320 100644 (file)
-/*\r
-       SHARP MZ-800 Emulator 'EmuZ-800'\r
-       SHARP MZ-1500 Emulator 'EmuZ-1500'\r
-       SHARP MZ-2200 Emulator 'EmuZ-2200'\r
-\r
-       Author : Takeda.Toshiya\r
-       Date   : 2011.02.17-\r
-\r
-       [ quick disk ]\r
-*/\r
-\r
-#include "quickdisk.h"\r
-#include "../z80sio.h"\r
-#include "../../fileio.h"\r
-\r
-#define MZT_HEADER_SIZE        128\r
-#define HEADER_SIZE    64\r
-\r
-#define EVENT_RESTORE  0\r
-#define EVENT_END      1\r
-\r
-// 100usec\r
-#define PERIOD_RESTORE 100\r
-// 1sec\r
-#define PERIOD_END     1000000\r
-\r
-#define DATA_SYNC      0x16\r
-#define DATA_MARK      0xa5\r
-#define DATA_CRC       0xff\r
-#define DATA_BREAK     0x100\r
-#define DATA_EMPTY     0x101\r
-\r
-#define HEADER_BLOCK_ID        0\r
-#define DATA_BLOCK_ID  1\r
-\r
-void QUICKDISK::initialize()\r
-{\r
-       insert = protect = false;\r
-       home = true;\r
-       first_data = send_break = true;\r
-}\r
-\r
-void QUICKDISK::release()\r
-{\r
-       release_disk();\r
-}\r
-\r
-void QUICKDISK::reset()\r
-{\r
-       wrga = mton = true;\r
-       sync = false;\r
-       motor_on = false;\r
-       accessed = false;\r
-       restore_id = end_id = -1;\r
-       \r
-       set_insert(insert);\r
-       set_protect(protect);\r
-       set_home(true);\r
-}\r
-\r
-/*\r
-       PROTECT -> CTSA\r
-               H: write protected\r
-       INSERT -> DCDA\r
-               L: inserted\r
-       HOME -> DCDB\r
-               L: reach to head position\r
-               H: reset, reach to end of disk, DTRB is L->H\r
-\r
-       RTSA -> WRGA\r
-               L: write disk / stop motor at the end of disk\r
-               H: read disk\r
-       DTRB -> MTON\r
-               H->L: start motor\r
-               H: stop motor at the end of disk\r
-*/\r
-\r
-#define REGISTER_RESTORE_EVENT() { \\r
-       if(restore_id == -1) { \\r
-               register_event(this, EVENT_RESTORE, PERIOD_RESTORE, false, &restore_id); \\r
-       } \\r
-}\r
-\r
-#define CANCEL_RESTORE_EVENT() { \\r
-       if(restore_id != -1) { \\r
-               cancel_event(this, restore_id); \\r
-               restore_id = -1; \\r
-       } \\r
-}\r
-\r
-#define REGISTER_END_EVENT() { \\r
-       if(end_id != -1) { \\r
-               cancel_event(this, end_id); \\r
-       } \\r
-       register_event(this, EVENT_END, PERIOD_END, false, &end_id); \\r
-}\r
-\r
-#define CANCEL_END_EVENT() { \\r
-       if(end_id != -1) { \\r
-               cancel_event(this, end_id); \\r
-               end_id = -1; \\r
-       } \\r
-}\r
-\r
-#define WRITE_BUFFER(v) { \\r
-       if(buffer_ptr < QUICKDISK_BUFFER_SIZE) { \\r
-               if(buffer[buffer_ptr] != v) { \\r
-                       buffer[buffer_ptr] = v; \\r
-                       modified = true; \\r
-               } \\r
-               buffer_ptr++; \\r
-       } \\r
-}\r
-\r
-void QUICKDISK::write_signal(int id, uint32 data, uint32 mask)\r
-{\r
-       bool next = ((data & mask) != 0);\r
-       \r
-       if(id == QUICKDISK_SIO_RTSA) {\r
-               if(wrga && !next) {\r
-                       // start to write\r
-                       first_data = true;\r
-                       write_ptr = 0;\r
-               } else if(!wrga && next) {\r
-                       // end to write\r
-                       write_crc();\r
-               }\r
-               wrga = next;\r
-       } else if(id == QUICKDISK_SIO_DTRB) {\r
-               if(mton && !next) {\r
-                       // H->L: start motor\r
-                       if(motor_on && wrga) {\r
-                               // restart to send\r
-                               send_data();\r
-                               REGISTER_END_EVENT();\r
-                       } else {\r
-                               // start motor and restore to home position\r
-                               motor_on = true;\r
-                               REGISTER_RESTORE_EVENT();\r
-                               CANCEL_END_EVENT();\r
-                       }\r
-               } else if(!mton && next) {\r
-                       // L->H: home signal is high\r
-                       set_home(true);\r
-               }\r
-               mton = next;\r
-       } else if(id == QUICKDISK_SIO_SYNC) {\r
-               // enter hunt/sync phase\r
-               sync = next;\r
-               if(sync) {\r
-                       // hack: start to send for verify\r
-                       if(!wrga) {\r
-                               write_crc();\r
-                               wrga = true;\r
-                       }\r
-                       send_data();\r
-               }\r
-       } else if(id == QUICKDISK_SIO_RXDONE) {\r
-               // send next data\r
-               send_data();\r
-       } else if(id == QUICKDISK_SIO_DATA || id == QUICKDISK_SIO_BREAK) {\r
-               // write data\r
-               if(!(motor_on && !wrga)) {\r
-                       return;\r
-               }\r
-               if(id == QUICKDISK_SIO_DATA) {\r
-                       if(first_data) {\r
-                               // write sync chars at the top of message\r
-                               WRITE_BUFFER(DATA_SYNC);\r
-                               WRITE_BUFFER(DATA_SYNC);\r
-                               first_data = false;\r
-                       }\r
-                       WRITE_BUFFER(data);\r
-                       write_ptr = buffer_ptr;\r
-               } else if(id == QUICKDISK_SIO_BREAK) {\r
-                       write_crc();\r
-                       WRITE_BUFFER(DATA_BREAK);\r
-                       first_data = true;\r
-                       write_ptr = 0;\r
-               }\r
-               accessed = true;\r
-               \r
-               if(buffer_ptr < QUICKDISK_BUFFER_SIZE) {\r
-                       REGISTER_END_EVENT();\r
-               } else {\r
-                       CANCEL_END_EVENT();\r
-                       end_of_disk();\r
-               }\r
-       }\r
-}\r
-\r
-uint32 QUICKDISK::read_signal(int ch)\r
-{\r
-       // access lamp signal\r
-       if(accessed) {\r
-               accessed = false;\r
-               return 1;\r
-       }\r
-       return 0;\r
-}\r
-\r
-void QUICKDISK::event_callback(int event_id, int err)\r
-{\r
-       if(event_id == EVENT_RESTORE) {\r
-               // reached to home position\r
-               restore_id = -1;\r
-               restore();\r
-       } else if(event_id == EVENT_END) {\r
-               // reached to end of disk\r
-               end_id = -1;\r
-               end_of_disk();\r
-       }\r
-}\r
-\r
-void QUICKDISK::restore()\r
-{\r
-       // reached to home position\r
-       set_home(false);\r
-       buffer_ptr = 0;\r
-       first_data = send_break = true;\r
-       \r
-       // start to send\r
-       send_data();\r
-}\r
-\r
-void QUICKDISK::send_data()\r
-{\r
-       if(!(motor_on && wrga) || restore_id != -1) {\r
-               return;\r
-       }\r
-retry:\r
-       if(buffer_ptr < QUICKDISK_BUFFER_SIZE && buffer[buffer_ptr] != DATA_EMPTY) {\r
-               if(buffer[buffer_ptr] == DATA_BREAK) {\r
-                       // send break signal\r
-                       if(send_break) {\r
-                               d_sio->write_signal(SIG_Z80SIO_BREAK_CH0, 1, 1);\r
-                               send_break = false;\r
-                       }\r
-                       // wait until sio enters hunt/sync phase\r
-                       if(!sync) {\r
-                               return;\r
-                       }\r
-                       buffer_ptr++;\r
-                       goto retry;\r
-               }\r
-               // send data\r
-               d_sio->write_signal(SIG_Z80SIO_RECV_CH0, buffer[buffer_ptr++], 0xff);\r
-               send_break = true;\r
-               accessed = true;\r
-               REGISTER_END_EVENT();\r
-       } else {\r
-               // reached to end of disk\r
-               CANCEL_END_EVENT();\r
-               end_of_disk();\r
-       }\r
-}\r
-\r
-void QUICKDISK::write_crc()\r
-{\r
-       if(!wrga && write_ptr != 0) {\r
-               buffer_ptr = write_ptr;\r
-               \r
-               WRITE_BUFFER(DATA_CRC);\r
-               WRITE_BUFFER(DATA_CRC);\r
-               WRITE_BUFFER(DATA_SYNC);\r
-               WRITE_BUFFER(DATA_SYNC);\r
-               // don't increment pointer !!!\r
-               WRITE_BUFFER(DATA_BREAK);\r
-               buffer_ptr--;\r
-       }\r
-       write_ptr = 0;\r
-}\r
-\r
-void QUICKDISK::end_of_disk()\r
-{\r
-       // write crc\r
-       write_crc();\r
-       \r
-       // reached to end of disk\r
-       if(mton || !wrga) {\r
-               motor_on = false;\r
-       } else {\r
-               REGISTER_RESTORE_EVENT();\r
-       }\r
-       set_home(true);\r
-}\r
-\r
-void QUICKDISK::set_insert(bool val)\r
-{\r
-       // L=inserted\r
-       d_sio->write_signal(SIG_Z80SIO_DCD_CH0, val ? 0 : 1, 1);\r
-       insert = val;\r
-}\r
-\r
-void QUICKDISK::set_protect(bool val)\r
-{\r
-       // H=protected\r
-       d_sio->write_signal(SIG_Z80SIO_CTS_CH0, val ? 1 : 0, 1);\r
-       protect = val;\r
-}\r
-\r
-void QUICKDISK::set_home(bool val)\r
-{\r
-       if(home != val) {\r
-               d_sio->write_signal(SIG_Z80SIO_DCD_CH1, val ? 1 : 0, 1);\r
-               home = val;\r
-       }\r
-}\r
-\r
-void QUICKDISK::open_disk(_TCHAR path[])\r
-{\r
-       // check current disk image\r
-       if(insert) {\r
-               if(_tcsicmp(file_path, path) == 0) {\r
-                       return;\r
-               }\r
-               // close current disk\r
-               close_disk();\r
-       }\r
-       memset(buffer, 0, sizeof(buffer));\r
-       \r
-       // load disk image\r
-       FILEIO* fio = new FILEIO();\r
-       if(fio->Fopen(path, FILEIO_READ_BINARY)) {\r
-               _tcscpy_s(file_path, _MAX_PATH, path);\r
-               \r
-               // clear buffer\r
-               for(int i = 0; i < QUICKDISK_BUFFER_SIZE; i++) {\r
-                       buffer[i] = DATA_EMPTY;\r
-               }\r
-               buffer_ptr = 0;\r
-               modified = false;\r
-               \r
-               // check extension\r
-               if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {\r
-                       // load mzt file\r
-                       fio->Fseek(0, FILEIO_SEEK_END);\r
-                       int remain = fio->Ftell();\r
-                       fio->Fseek(0, FILEIO_SEEK_SET);\r
-                       \r
-                       int num_block = 0;\r
-                       int block_num_ptr = 0;\r
-                       \r
-                       // create block file\r
-                       buffer[buffer_ptr++] = DATA_BREAK;\r
-                       buffer[buffer_ptr++] = DATA_SYNC;\r
-                       buffer[buffer_ptr++] = DATA_SYNC;\r
-                       buffer[buffer_ptr++] = DATA_MARK;\r
-                       block_num_ptr = buffer_ptr;\r
-                       buffer[buffer_ptr++] = 0; // block number\r
-                       buffer[buffer_ptr++] = DATA_CRC;\r
-                       buffer[buffer_ptr++] = DATA_CRC;\r
-                       buffer[buffer_ptr++] = DATA_SYNC;\r
-                       buffer[buffer_ptr++] = DATA_SYNC;\r
-                       buffer[buffer_ptr++] = DATA_BREAK;\r
-                       \r
-                       while(remain >= MZT_HEADER_SIZE) {\r
-                               // load header\r
-                               uint8 header[MZT_HEADER_SIZE], ram[0x20000];\r
-                               fio->Fread(header, MZT_HEADER_SIZE, 1);\r
-                               remain -= MZT_HEADER_SIZE;\r
-                               \r
-                               // load data\r
-                               int size = header[0x12] | (header[0x13] << 8);\r
-                               int offs = header[0x14] | (header[0x15] << 8);\r
-                               memset(ram, 0, sizeof(ram));\r
-                               fio->Fread(ram + offs, size, 1);\r
-                               remain -= size;\r
-#if 0\r
-                               // apply mz700win patch\r
-                               if(header[0x40] == 'P' && header[0x41] == 'A' && header[0x42] == 'T' && header[0x43] == ':') {\r
-                                       int patch_ofs = 0x44;\r
-                                       for(; patch_ofs < 0x80; ) {\r
-                                               uint16 patch_addr = header[patch_ofs] | (header[patch_ofs + 1] << 8);\r
-                                               patch_ofs += 2;\r
-                                               if(patch_addr == 0xffff) {\r
-                                                       break;\r
-                                               }\r
-                                               int patch_len = header[patch_ofs++];\r
-                                               for(int i = 0; i < patch_len; i++) {\r
-                                                       ram[patch_addr + i] = header[patch_ofs++];\r
-                                               }\r
-                                       }\r
-                                       // clear patch data\r
-                                       for(int i = 0x40; i < patch_ofs; i++) {\r
-                                               header[i] = 0;\r
-                                       }\r
-                               }\r
-#endif\r
-                               // copy header\r
-                               buffer[block_num_ptr] = ++num_block;\r
-                               \r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_MARK;\r
-                               buffer[buffer_ptr++] = HEADER_BLOCK_ID;\r
-                               buffer[buffer_ptr++] = HEADER_SIZE;\r
-                               buffer[buffer_ptr++] = 0;\r
-                               buffer[buffer_ptr++] = header[0];       // attribute\r
-                               for(int i = 0; i < 17; i++) {\r
-                                       buffer[buffer_ptr++] = header[i + 1]; // file name\r
-                               }\r
-                               buffer[buffer_ptr++] = header[0x3e];    // lock\r
-                               buffer[buffer_ptr++] = header[0x3f];    // secret\r
-                               buffer[buffer_ptr++] = header[0x12];    // file size\r
-                               buffer[buffer_ptr++] = header[0x13];\r
-                               buffer[buffer_ptr++] = header[0x14];    // load addr\r
-                               buffer[buffer_ptr++] = header[0x15];\r
-                               buffer[buffer_ptr++] = header[0x16];    // exec addr\r
-                               buffer[buffer_ptr++] = header[0x17];\r
-                               for(int i = 26; i < HEADER_SIZE; i++) {\r
-                                       buffer[buffer_ptr++] = 0;       // comment\r
-                               }\r
-                               buffer[buffer_ptr++] = DATA_CRC;\r
-                               buffer[buffer_ptr++] = DATA_CRC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_BREAK;\r
-                               \r
-                               // copy data\r
-                               buffer[block_num_ptr] = ++num_block;\r
-                               \r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_MARK;\r
-                               buffer[buffer_ptr++] = DATA_BLOCK_ID;\r
-                               buffer[buffer_ptr++] = (uint8)(size & 0xff);\r
-                               buffer[buffer_ptr++] = (uint8)(size >> 8);\r
-                               for(int i = 0; i < size; i++) {\r
-                                       buffer[buffer_ptr++] = ram[offs + i];\r
-                               }\r
-                               buffer[buffer_ptr++] = DATA_CRC;\r
-                               buffer[buffer_ptr++] = DATA_CRC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                               buffer[buffer_ptr++] = DATA_BREAK;\r
-                       }\r
-               } else {\r
-                       // check header\r
-                       uint8 header[16];\r
-                       fio->Fread(header, sizeof(header), 1);\r
-                       if(memcmp(header, "-QD format-", 11) != 0) {\r
-                               fio->Fseek(0, FILEIO_SEEK_SET);\r
-                       }\r
-                       \r
-                       // load raw file\r
-                       bool in_gap = true;\r
-                       int sync_top_ptr = 0, sync_num = 0, sync_num_prev = 0, data;\r
-                       \r
-                       buffer[buffer_ptr++] = DATA_BREAK;\r
-                       \r
-                       while((data = fio->Fgetc()) != EOF) {\r
-                               if(data == DATA_SYNC) {\r
-                                       if(sync_num == 0) {\r
-                                               sync_top_ptr = buffer_ptr;\r
-                                       }\r
-                                       sync_num++;\r
-                               } else {\r
-                                       sync_num_prev = sync_num;\r
-                                       sync_num = 0;\r
-                               }\r
-                               if(in_gap) {\r
-                                       if(sync_num_prev >= 4 && sync_num == 0) {\r
-                                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                                               buffer[buffer_ptr++] = data;\r
-                                               in_gap = false;\r
-                                       }\r
-                               } else {\r
-                                       if(sync_num_prev >= 4 && sync_num == 0 && data == 0x00) {\r
-                                               buffer_ptr = sync_top_ptr;\r
-                                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                                               buffer[buffer_ptr++] = DATA_SYNC;\r
-                                               buffer[buffer_ptr++] = DATA_BREAK;\r
-                                               in_gap = true;\r
-                                       } else {\r
-                                               buffer[buffer_ptr++] = data;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               set_insert(true);\r
-               set_protect(fio->IsProtected(path));\r
-               set_home(true);\r
-               \r
-               fio->Fclose();\r
-       }\r
-       delete fio;\r
-}\r
-\r
-void QUICKDISK::close_disk()\r
-{\r
-       release_disk();\r
-       set_insert(false);\r
-       set_protect(false);\r
-       set_home(true);\r
-       \r
-       // cancel all events\r
-       CANCEL_RESTORE_EVENT();\r
-       CANCEL_END_EVENT();\r
-}\r
-\r
-void QUICKDISK::release_disk()\r
-{\r
-       if(insert && !protect && modified) {\r
-               // check extension\r
-               _TCHAR file_path_tmp[_MAX_PATH];\r
-               if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {\r
-                       _tcscpy_s(file_path_tmp, _MAX_PATH, file_path);\r
-               } else {\r
-                       _stprintf_s(file_path_tmp, _MAX_PATH, _T("%s.mzt"), get_file_path_without_extensiton(file_path));\r
-               }\r
-               // save blocks as mzt file\r
-               FILEIO* fio = new FILEIO();\r
-               if(fio->Fopen(file_path_tmp, FILEIO_WRITE_BINARY)) {\r
-                       int block_num = buffer[4];\r
-                       buffer_ptr = 10;\r
-                       \r
-                       for(int i = 0; i < block_num; i++) {\r
-                               if(buffer[buffer_ptr] == DATA_EMPTY) {\r
-                                       break;\r
-                               }\r
-                               int id = buffer[buffer_ptr + 3] & 3;\r
-                               int size = buffer[buffer_ptr + 4] | (buffer[buffer_ptr + 5] << 8);\r
-                               buffer_ptr += 6;\r
-                               \r
-                               if(id == HEADER_BLOCK_ID) {\r
-                                       // create mzt header\r
-                                       uint8 header[MZT_HEADER_SIZE];\r
-                                       memset(header, 0, sizeof(header));\r
-                                       \r
-                                       header[0x00] = (uint8)buffer[buffer_ptr + 0];   // attribute\r
-                                       for(int i = 1; i <= 17; i++) {\r
-                                               header[i] = (uint8)buffer[buffer_ptr + i];      // file name\r
-                                       }\r
-                                       header[0x3e] = (uint8)buffer[buffer_ptr + 18];  // lock\r
-                                       header[0x3f] = (uint8)buffer[buffer_ptr + 19];  // lock\r
-                                       header[0x12] = (uint8)buffer[buffer_ptr + 20];  // file size\r
-                                       header[0x13] = (uint8)buffer[buffer_ptr + 21];\r
-                                       header[0x14] = (uint8)buffer[buffer_ptr + 22];  // load addr\r
-                                       header[0x15] = (uint8)buffer[buffer_ptr + 23];\r
-                                       header[0x16] = (uint8)buffer[buffer_ptr + 24];  // exec addr\r
-                                       header[0x17] = (uint8)buffer[buffer_ptr + 25];\r
-                                       fio->Fwrite(header, MZT_HEADER_SIZE, 1);\r
-                               } else {\r
-                                       // data\r
-                                       for(int i = 0; i < size; i++) {\r
-                                               fio->Fputc(buffer[buffer_ptr + i]);\r
-                                       }\r
-                               }\r
-                               buffer_ptr += size + 5;\r
-                       }\r
-                       fio->Fclose();\r
-               }\r
-               delete fio;\r
-       }\r
-}\r
-\r
-#define STATE_VERSION  1\r
-\r
-void QUICKDISK::save_state(FILEIO* state_fio)\r
-{\r
-       state_fio->FputUint32(STATE_VERSION);\r
-       state_fio->FputInt32(this_device_id);\r
-       \r
-       state_fio->Fwrite(file_path, sizeof(file_path), 1);\r
-       state_fio->FputBool(insert);\r
-       state_fio->FputBool(protect);\r
-       state_fio->FputBool(home);\r
-       state_fio->FputBool(modified);\r
-       state_fio->FputBool(accessed);\r
-       state_fio->Fwrite(buffer, sizeof(buffer), 1);\r
-       state_fio->FputInt32(buffer_ptr);\r
-       state_fio->FputInt32(write_ptr);\r
-       state_fio->FputBool(first_data);\r
-       state_fio->FputBool(send_break);\r
-       state_fio->FputBool(wrga);\r
-       state_fio->FputBool(mton);\r
-       state_fio->FputBool(sync);\r
-       state_fio->FputBool(motor_on);\r
-       state_fio->FputInt32(restore_id);\r
-       state_fio->FputInt32(end_id);\r
-}\r
-\r
-bool QUICKDISK::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
-       state_fio->Fread(file_path, sizeof(file_path), 1);\r
-       insert = state_fio->FgetBool();\r
-       protect = state_fio->FgetBool();\r
-       home = state_fio->FgetBool();\r
-       modified = state_fio->FgetBool();\r
-       accessed = state_fio->FgetBool();\r
-       state_fio->Fread(buffer, sizeof(buffer), 1);\r
-       buffer_ptr = state_fio->FgetInt32();\r
-       write_ptr = state_fio->FgetInt32();\r
-       first_data = state_fio->FgetBool();\r
-       send_break = state_fio->FgetBool();\r
-       wrga = state_fio->FgetBool();\r
-       mton = state_fio->FgetBool();\r
-       sync = state_fio->FgetBool();\r
-       motor_on = state_fio->FgetBool();\r
-       restore_id = state_fio->FgetInt32();\r
-       end_id = state_fio->FgetInt32();\r
-       return true;\r
-}\r
-\r
+/*
+       SHARP MZ-800 Emulator 'EmuZ-800'
+       SHARP MZ-1500 Emulator 'EmuZ-1500'
+       SHARP MZ-2200 Emulator 'EmuZ-2200'
+
+       Author : Takeda.Toshiya
+       Date   : 2011.02.17-
+
+       [ quick disk ]
+*/
+
+#include "quickdisk.h"
+#include "../z80sio.h"
+#include "../../fileio.h"
+
+#define MZT_HEADER_SIZE        128
+#define HEADER_SIZE    64
+
+#define EVENT_RESTORE  0
+#define EVENT_END      1
+
+// 100usec
+#define PERIOD_RESTORE 100
+// 1sec
+#define PERIOD_END     1000000
+
+#define DATA_SYNC      0x16
+#define DATA_MARK      0xa5
+#define DATA_CRC       0xff
+#define DATA_BREAK     0x100
+#define DATA_EMPTY     0x101
+
+#define HEADER_BLOCK_ID        0
+#define DATA_BLOCK_ID  1
+
+void QUICKDISK::initialize()
+{
+       insert = protect = false;
+       home = true;
+       first_data = send_break = true;
+}
+
+void QUICKDISK::release()
+{
+       release_disk();
+}
+
+void QUICKDISK::reset()
+{
+       wrga = mton = true;
+       sync = false;
+       motor_on = false;
+       accessed = false;
+       restore_id = end_id = -1;
+       
+       set_insert(insert);
+       set_protect(protect);
+       set_home(true);
+}
+
+/*
+       PROTECT -> CTSA
+               H: write protected
+       INSERT -> DCDA
+               L: inserted
+       HOME -> DCDB
+               L: reach to head position
+               H: reset, reach to end of disk, DTRB is L->H
+
+       RTSA -> WRGA
+               L: write disk / stop motor at the end of disk
+               H: read disk
+       DTRB -> MTON
+               H->L: start motor
+               H: stop motor at the end of disk
+*/
+
+#define REGISTER_RESTORE_EVENT() { \
+       if(restore_id == -1) { \
+               register_event(this, EVENT_RESTORE, PERIOD_RESTORE, false, &restore_id); \
+       } \
+}
+
+#define CANCEL_RESTORE_EVENT() { \
+       if(restore_id != -1) { \
+               cancel_event(this, restore_id); \
+               restore_id = -1; \
+       } \
+}
+
+#define REGISTER_END_EVENT() { \
+       if(end_id != -1) { \
+               cancel_event(this, end_id); \
+       } \
+       register_event(this, EVENT_END, PERIOD_END, false, &end_id); \
+}
+
+#define CANCEL_END_EVENT() { \
+       if(end_id != -1) { \
+               cancel_event(this, end_id); \
+               end_id = -1; \
+       } \
+}
+
+#define WRITE_BUFFER(v) { \
+       if(buffer_ptr < QUICKDISK_BUFFER_SIZE) { \
+               if(buffer[buffer_ptr] != v) { \
+                       buffer[buffer_ptr] = v; \
+                       modified = true; \
+               } \
+               buffer_ptr++; \
+       } \
+}
+
+void QUICKDISK::write_signal(int id, uint32 data, uint32 mask)
+{
+       bool next = ((data & mask) != 0);
+       
+       if(id == QUICKDISK_SIO_RTSA) {
+               if(wrga && !next) {
+                       // start to write
+                       first_data = true;
+                       write_ptr = 0;
+               } else if(!wrga && next) {
+                       // end to write
+                       write_crc();
+               }
+               wrga = next;
+       } else if(id == QUICKDISK_SIO_DTRB) {
+               if(mton && !next) {
+                       // H->L: start motor
+                       if(motor_on && wrga) {
+                               // restart to send
+                               send_data();
+                               REGISTER_END_EVENT();
+                       } else {
+                               // start motor and restore to home position
+                               motor_on = true;
+                               REGISTER_RESTORE_EVENT();
+                               CANCEL_END_EVENT();
+                       }
+               } else if(!mton && next) {
+                       // L->H: home signal is high
+                       set_home(true);
+               }
+               mton = next;
+       } else if(id == QUICKDISK_SIO_SYNC) {
+               // enter hunt/sync phase
+               sync = next;
+               if(sync) {
+                       // hack: start to send for verify
+                       if(!wrga) {
+                               write_crc();
+                               wrga = true;
+                       }
+                       send_data();
+               }
+       } else if(id == QUICKDISK_SIO_RXDONE) {
+               // send next data
+               send_data();
+       } else if(id == QUICKDISK_SIO_DATA || id == QUICKDISK_SIO_BREAK) {
+               // write data
+               if(!(motor_on && !wrga)) {
+                       return;
+               }
+               if(id == QUICKDISK_SIO_DATA) {
+                       if(first_data) {
+                               // write sync chars at the top of message
+                               WRITE_BUFFER(DATA_SYNC);
+                               WRITE_BUFFER(DATA_SYNC);
+                               first_data = false;
+                       }
+                       WRITE_BUFFER(data);
+                       write_ptr = buffer_ptr;
+               } else if(id == QUICKDISK_SIO_BREAK) {
+                       write_crc();
+                       WRITE_BUFFER(DATA_BREAK);
+                       first_data = true;
+                       write_ptr = 0;
+               }
+               accessed = true;
+               
+               if(buffer_ptr < QUICKDISK_BUFFER_SIZE) {
+                       REGISTER_END_EVENT();
+               } else {
+                       CANCEL_END_EVENT();
+                       end_of_disk();
+               }
+       }
+}
+
+uint32 QUICKDISK::read_signal(int ch)
+{
+       // access lamp signal
+       if(accessed) {
+               accessed = false;
+               return 1;
+       }
+       return 0;
+}
+
+void QUICKDISK::event_callback(int event_id, int err)
+{
+       if(event_id == EVENT_RESTORE) {
+               // reached to home position
+               restore_id = -1;
+               restore();
+       } else if(event_id == EVENT_END) {
+               // reached to end of disk
+               end_id = -1;
+               end_of_disk();
+       }
+}
+
+void QUICKDISK::restore()
+{
+       // reached to home position
+       set_home(false);
+       buffer_ptr = 0;
+       first_data = send_break = true;
+       
+       // start to send
+       send_data();
+}
+
+void QUICKDISK::send_data()
+{
+       if(!(motor_on && wrga) || restore_id != -1) {
+               return;
+       }
+retry:
+       if(buffer_ptr < QUICKDISK_BUFFER_SIZE && buffer[buffer_ptr] != DATA_EMPTY) {
+               if(buffer[buffer_ptr] == DATA_BREAK) {
+                       // send break signal
+                       if(send_break) {
+                               d_sio->write_signal(SIG_Z80SIO_BREAK_CH0, 1, 1);
+                               send_break = false;
+                       }
+                       // wait until sio enters hunt/sync phase
+                       if(!sync) {
+                               return;
+                       }
+                       buffer_ptr++;
+                       goto retry;
+               }
+               // send data
+               d_sio->write_signal(SIG_Z80SIO_RECV_CH0, buffer[buffer_ptr++], 0xff);
+               send_break = true;
+               accessed = true;
+               REGISTER_END_EVENT();
+       } else {
+               // reached to end of disk
+               CANCEL_END_EVENT();
+               end_of_disk();
+       }
+}
+
+void QUICKDISK::write_crc()
+{
+       if(!wrga && write_ptr != 0) {
+               buffer_ptr = write_ptr;
+               
+               WRITE_BUFFER(DATA_CRC);
+               WRITE_BUFFER(DATA_CRC);
+               WRITE_BUFFER(DATA_SYNC);
+               WRITE_BUFFER(DATA_SYNC);
+               // don't increment pointer !!!
+               WRITE_BUFFER(DATA_BREAK);
+               buffer_ptr--;
+       }
+       write_ptr = 0;
+}
+
+void QUICKDISK::end_of_disk()
+{
+       // write crc
+       write_crc();
+       
+       // reached to end of disk
+       if(mton || !wrga) {
+               motor_on = false;
+       } else {
+               REGISTER_RESTORE_EVENT();
+       }
+       set_home(true);
+}
+
+void QUICKDISK::set_insert(bool val)
+{
+       // L=inserted
+       d_sio->write_signal(SIG_Z80SIO_DCD_CH0, val ? 0 : 1, 1);
+       insert = val;
+}
+
+void QUICKDISK::set_protect(bool val)
+{
+       // H=protected
+       d_sio->write_signal(SIG_Z80SIO_CTS_CH0, val ? 1 : 0, 1);
+       protect = val;
+}
+
+void QUICKDISK::set_home(bool val)
+{
+       if(home != val) {
+               d_sio->write_signal(SIG_Z80SIO_DCD_CH1, val ? 1 : 0, 1);
+               home = val;
+       }
+}
+
+void QUICKDISK::open_disk(_TCHAR path[])
+{
+       // check current disk image
+       if(insert) {
+               if(_tcsicmp(file_path, path) == 0) {
+                       return;
+               }
+               // close current disk
+               close_disk();
+       }
+       memset(buffer, 0, sizeof(buffer));
+       
+       // load disk image
+       FILEIO* fio = new FILEIO();
+       if(fio->Fopen(path, FILEIO_READ_BINARY)) {
+               _tcscpy_s(file_path, _MAX_PATH, path);
+               
+               // clear buffer
+               for(int i = 0; i < QUICKDISK_BUFFER_SIZE; i++) {
+                       buffer[i] = DATA_EMPTY;
+               }
+               buffer_ptr = 0;
+               modified = false;
+               
+               // check extension
+               if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {
+                       // load mzt file
+                       fio->Fseek(0, FILEIO_SEEK_END);
+                       int remain = fio->Ftell();
+                       fio->Fseek(0, FILEIO_SEEK_SET);
+                       
+                       int num_block = 0;
+                       int block_num_ptr = 0;
+                       
+                       // create block file
+                       buffer[buffer_ptr++] = DATA_BREAK;
+                       buffer[buffer_ptr++] = DATA_SYNC;
+                       buffer[buffer_ptr++] = DATA_SYNC;
+                       buffer[buffer_ptr++] = DATA_MARK;
+                       block_num_ptr = buffer_ptr;
+                       buffer[buffer_ptr++] = 0; // block number
+                       buffer[buffer_ptr++] = DATA_CRC;
+                       buffer[buffer_ptr++] = DATA_CRC;
+                       buffer[buffer_ptr++] = DATA_SYNC;
+                       buffer[buffer_ptr++] = DATA_SYNC;
+                       buffer[buffer_ptr++] = DATA_BREAK;
+                       
+                       while(remain >= MZT_HEADER_SIZE) {
+                               // load header
+                               uint8 header[MZT_HEADER_SIZE], ram[0x20000];
+                               fio->Fread(header, MZT_HEADER_SIZE, 1);
+                               remain -= MZT_HEADER_SIZE;
+                               
+                               // load data
+                               int size = header[0x12] | (header[0x13] << 8);
+                               int offs = header[0x14] | (header[0x15] << 8);
+                               memset(ram, 0, sizeof(ram));
+                               fio->Fread(ram + offs, size, 1);
+                               remain -= size;
+#if 0
+                               // apply mz700win patch
+                               if(header[0x40] == 'P' && header[0x41] == 'A' && header[0x42] == 'T' && header[0x43] == ':') {
+                                       int patch_ofs = 0x44;
+                                       for(; patch_ofs < 0x80; ) {
+                                               uint16 patch_addr = header[patch_ofs] | (header[patch_ofs + 1] << 8);
+                                               patch_ofs += 2;
+                                               if(patch_addr == 0xffff) {
+                                                       break;
+                                               }
+                                               int patch_len = header[patch_ofs++];
+                                               for(int i = 0; i < patch_len; i++) {
+                                                       ram[patch_addr + i] = header[patch_ofs++];
+                                               }
+                                       }
+                                       // clear patch data
+                                       for(int i = 0x40; i < patch_ofs; i++) {
+                                               header[i] = 0;
+                                       }
+                               }
+#endif
+                               // copy header
+                               buffer[block_num_ptr] = ++num_block;
+                               
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_MARK;
+                               buffer[buffer_ptr++] = HEADER_BLOCK_ID;
+                               buffer[buffer_ptr++] = HEADER_SIZE;
+                               buffer[buffer_ptr++] = 0;
+                               buffer[buffer_ptr++] = header[0];       // attribute
+                               for(int i = 0; i < 17; i++) {
+                                       buffer[buffer_ptr++] = header[i + 1]; // file name
+                               }
+                               buffer[buffer_ptr++] = header[0x3e];    // lock
+                               buffer[buffer_ptr++] = header[0x3f];    // secret
+                               buffer[buffer_ptr++] = header[0x12];    // file size
+                               buffer[buffer_ptr++] = header[0x13];
+                               buffer[buffer_ptr++] = header[0x14];    // load addr
+                               buffer[buffer_ptr++] = header[0x15];
+                               buffer[buffer_ptr++] = header[0x16];    // exec addr
+                               buffer[buffer_ptr++] = header[0x17];
+                               for(int i = 26; i < HEADER_SIZE; i++) {
+                                       buffer[buffer_ptr++] = 0;       // comment
+                               }
+                               buffer[buffer_ptr++] = DATA_CRC;
+                               buffer[buffer_ptr++] = DATA_CRC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_BREAK;
+                               
+                               // copy data
+                               buffer[block_num_ptr] = ++num_block;
+                               
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_MARK;
+                               buffer[buffer_ptr++] = DATA_BLOCK_ID;
+                               buffer[buffer_ptr++] = (uint8)(size & 0xff);
+                               buffer[buffer_ptr++] = (uint8)(size >> 8);
+                               for(int i = 0; i < size; i++) {
+                                       buffer[buffer_ptr++] = ram[offs + i];
+                               }
+                               buffer[buffer_ptr++] = DATA_CRC;
+                               buffer[buffer_ptr++] = DATA_CRC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_SYNC;
+                               buffer[buffer_ptr++] = DATA_BREAK;
+                       }
+               } else {
+                       // check header
+                       uint8 header[16];
+                       fio->Fread(header, sizeof(header), 1);
+                       if(memcmp(header, "-QD format-", 11) != 0) {
+                               fio->Fseek(0, FILEIO_SEEK_SET);
+                       }
+                       
+                       // load raw file
+                       bool in_gap = true;
+                       int sync_top_ptr = 0, sync_num = 0, sync_num_prev = 0, data;
+                       
+                       buffer[buffer_ptr++] = DATA_BREAK;
+                       
+                       while((data = fio->Fgetc()) != EOF) {
+                               if(data == DATA_SYNC) {
+                                       if(sync_num == 0) {
+                                               sync_top_ptr = buffer_ptr;
+                                       }
+                                       sync_num++;
+                               } else {
+                                       sync_num_prev = sync_num;
+                                       sync_num = 0;
+                               }
+                               if(in_gap) {
+                                       if(sync_num_prev >= 4 && sync_num == 0) {
+                                               buffer[buffer_ptr++] = DATA_SYNC;
+                                               buffer[buffer_ptr++] = DATA_SYNC;
+                                               buffer[buffer_ptr++] = data;
+                                               in_gap = false;
+                                       }
+                               } else {
+                                       if(sync_num_prev >= 4 && sync_num == 0 && data == 0x00) {
+                                               buffer_ptr = sync_top_ptr;
+                                               buffer[buffer_ptr++] = DATA_SYNC;
+                                               buffer[buffer_ptr++] = DATA_SYNC;
+                                               buffer[buffer_ptr++] = DATA_BREAK;
+                                               in_gap = true;
+                                       } else {
+                                               buffer[buffer_ptr++] = data;
+                                       }
+                               }
+                       }
+               }
+               set_insert(true);
+               set_protect(fio->IsProtected(path));
+               set_home(true);
+               
+               fio->Fclose();
+       }
+       delete fio;
+}
+
+void QUICKDISK::close_disk()
+{
+       release_disk();
+       set_insert(false);
+       set_protect(false);
+       set_home(true);
+       
+       // cancel all events
+       CANCEL_RESTORE_EVENT();
+       CANCEL_END_EVENT();
+}
+
+void QUICKDISK::release_disk()
+{
+       if(insert && !protect && modified) {
+               // check extension
+               _TCHAR file_path_tmp[_MAX_PATH];
+               if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {
+                       _tcscpy_s(file_path_tmp, _MAX_PATH, file_path);
+               } else {
+                       _stprintf_s(file_path_tmp, _MAX_PATH, _T("%s.mzt"), get_file_path_without_extensiton(file_path));
+               }
+               // save blocks as mzt file
+               FILEIO* fio = new FILEIO();
+               if(fio->Fopen(file_path_tmp, FILEIO_WRITE_BINARY)) {
+                       int block_num = buffer[4];
+                       buffer_ptr = 10;
+                       
+                       for(int i = 0; i < block_num; i++) {
+                               if(buffer[buffer_ptr] == DATA_EMPTY) {
+                                       break;
+                               }
+                               int id = buffer[buffer_ptr + 3] & 3;
+                               int size = buffer[buffer_ptr + 4] | (buffer[buffer_ptr + 5] << 8);
+                               buffer_ptr += 6;
+                               
+                               if(id == HEADER_BLOCK_ID) {
+                                       // create mzt header
+                                       uint8 header[MZT_HEADER_SIZE];
+                                       memset(header, 0, sizeof(header));
+                                       
+                                       header[0x00] = (uint8)buffer[buffer_ptr + 0];   // attribute
+                                       for(int i = 1; i <= 17; i++) {
+                                               header[i] = (uint8)buffer[buffer_ptr + i];      // file name
+                                       }
+                                       header[0x3e] = (uint8)buffer[buffer_ptr + 18];  // lock
+                                       header[0x3f] = (uint8)buffer[buffer_ptr + 19];  // lock
+                                       header[0x12] = (uint8)buffer[buffer_ptr + 20];  // file size
+                                       header[0x13] = (uint8)buffer[buffer_ptr + 21];
+                                       header[0x14] = (uint8)buffer[buffer_ptr + 22];  // load addr
+                                       header[0x15] = (uint8)buffer[buffer_ptr + 23];
+                                       header[0x16] = (uint8)buffer[buffer_ptr + 24];  // exec addr
+                                       header[0x17] = (uint8)buffer[buffer_ptr + 25];
+                                       fio->Fwrite(header, MZT_HEADER_SIZE, 1);
+                               } else {
+                                       // data
+                                       for(int i = 0; i < size; i++) {
+                                               fio->Fputc(buffer[buffer_ptr + i]);
+                                       }
+                               }
+                               buffer_ptr += size + 5;
+                       }
+                       fio->Fclose();
+               }
+               delete fio;
+       }
+}
+
+#define STATE_VERSION  1
+
+void QUICKDISK::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->Fwrite(file_path, sizeof(file_path), 1);
+       state_fio->FputBool(insert);
+       state_fio->FputBool(protect);
+       state_fio->FputBool(home);
+       state_fio->FputBool(modified);
+       state_fio->FputBool(accessed);
+       state_fio->Fwrite(buffer, sizeof(buffer), 1);
+       state_fio->FputInt32(buffer_ptr);
+       state_fio->FputInt32(write_ptr);
+       state_fio->FputBool(first_data);
+       state_fio->FputBool(send_break);
+       state_fio->FputBool(wrga);
+       state_fio->FputBool(mton);
+       state_fio->FputBool(sync);
+       state_fio->FputBool(motor_on);
+       state_fio->FputInt32(restore_id);
+       state_fio->FputInt32(end_id);
+}
+
+bool QUICKDISK::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(file_path, sizeof(file_path), 1);
+       insert = state_fio->FgetBool();
+       protect = state_fio->FgetBool();
+       home = state_fio->FgetBool();
+       modified = state_fio->FgetBool();
+       accessed = state_fio->FgetBool();
+       state_fio->Fread(buffer, sizeof(buffer), 1);
+       buffer_ptr = state_fio->FgetInt32();
+       write_ptr = state_fio->FgetInt32();
+       first_data = state_fio->FgetBool();
+       send_break = state_fio->FgetBool();
+       wrga = state_fio->FgetBool();
+       mton = state_fio->FgetBool();
+       sync = state_fio->FgetBool();
+       motor_on = state_fio->FgetBool();
+       restore_id = state_fio->FgetInt32();
+       end_id = state_fio->FgetInt32();
+       return true;
+}
+