-/*\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;
+}
+