--- /dev/null
+/*\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(file_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(file_path_tmp, file_path);\r
+ } else {\r
+ _stprintf(file_path_tmp, _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