OSDN Git Service

[VM][I286] Save cpustate without StateBuffer().
[csp-qt/common_source_project-fm7.git] / source / src / vm / mz700 / quickdisk.cpp
1 /*
2         SHARP MZ-800 Emulator 'EmuZ-800'
3         SHARP MZ-1500 Emulator 'EmuZ-1500'
4         SHARP MZ-2200 Emulator 'EmuZ-2200'
5
6         Author : Takeda.Toshiya
7         Date   : 2011.02.17-
8
9         [ quick disk ]
10 */
11
12 #include "quickdisk.h"
13 #include "../z80sio.h"
14
15 #define MZT_HEADER_SIZE 128
16 #define HEADER_SIZE     64
17
18 #define EVENT_RESTORE   0
19 #define EVENT_END       1
20
21 // 100usec
22 #define PERIOD_RESTORE  100
23 // 1sec
24 #define PERIOD_END      1000000
25
26 #define DATA_SYNC       0x16
27 #define DATA_MARK       0xa5
28 #define DATA_CRC        0xff
29 #define DATA_BREAK      0x100
30 #define DATA_EMPTY      0x101
31
32 #define HEADER_BLOCK_ID 0
33 #define DATA_BLOCK_ID   1
34
35 void QUICKDISK::initialize()
36 {
37         insert = protect = false;
38         home = true;
39         first_data = send_break = true;
40 }
41
42 void QUICKDISK::release()
43 {
44         release_disk();
45 }
46
47 void QUICKDISK::reset()
48 {
49         wrga = mton = true;
50         sync = false;
51         motor_on = false;
52         accessed = false;
53         restore_id = end_id = -1;
54         
55         set_insert(insert);
56         set_protect(protect);
57         set_home(true);
58 }
59
60 /*
61         PROTECT -> CTSA
62                 H: write protected
63         INSERT -> DCDA
64                 L: inserted
65         HOME -> DCDB
66                 L: reach to head position
67                 H: reset, reach to end of disk, DTRB is L->H
68
69         RTSA -> WRGA
70                 L: write disk / stop motor at the end of disk
71                 H: read disk
72         DTRB -> MTON
73                 H->L: start motor
74                 H: stop motor at the end of disk
75 */
76
77 #define REGISTER_RESTORE_EVENT() { \
78         if(restore_id == -1) { \
79                 register_event(this, EVENT_RESTORE, PERIOD_RESTORE, false, &restore_id); \
80         } \
81 }
82
83 #define CANCEL_RESTORE_EVENT() { \
84         if(restore_id != -1) { \
85                 cancel_event(this, restore_id); \
86                 restore_id = -1; \
87         } \
88 }
89
90 #define REGISTER_END_EVENT() { \
91         if(end_id != -1) { \
92                 cancel_event(this, end_id); \
93         } \
94         register_event(this, EVENT_END, PERIOD_END, false, &end_id); \
95 }
96
97 #define CANCEL_END_EVENT() { \
98         if(end_id != -1) { \
99                 cancel_event(this, end_id); \
100                 end_id = -1; \
101         } \
102 }
103
104 #define WRITE_BUFFER(v) { \
105         if(buffer_ptr < QUICKDISK_BUFFER_SIZE) { \
106                 if(buffer[buffer_ptr] != v) { \
107                         buffer[buffer_ptr] = v; \
108                         modified = true; \
109                 } \
110                 buffer_ptr++; \
111         } \
112 }
113
114 void QUICKDISK::write_signal(int id, uint32_t data, uint32_t mask)
115 {
116         bool next = ((data & mask) != 0);
117         
118         if(id == QUICKDISK_SIO_RTSA) {
119                 if(wrga && !next) {
120                         // start to write
121                         first_data = true;
122                         write_ptr = 0;
123                 } else if(!wrga && next) {
124                         // end to write
125                         write_crc();
126                 }
127                 wrga = next;
128         } else if(id == QUICKDISK_SIO_DTRB) {
129                 if(mton && !next) {
130                         // H->L: start motor
131                         if(motor_on && wrga) {
132                                 // restart to send
133                                 send_data();
134                                 REGISTER_END_EVENT();
135                         } else {
136                                 // start motor and restore to home position
137                                 motor_on = true;
138                                 REGISTER_RESTORE_EVENT();
139                                 CANCEL_END_EVENT();
140                         }
141                 } else if(!mton && next) {
142                         // L->H: home signal is high
143                         set_home(true);
144                 }
145                 mton = next;
146         } else if(id == QUICKDISK_SIO_SYNC) {
147                 // enter hunt/sync phase
148                 sync = next;
149                 if(sync) {
150                         // hack: start to send for verify
151                         if(!wrga) {
152                                 write_crc();
153                                 wrga = true;
154                         }
155                         send_data();
156                 }
157         } else if(id == QUICKDISK_SIO_RXDONE) {
158                 // send next data
159                 send_data();
160         } else if(id == QUICKDISK_SIO_DATA || id == QUICKDISK_SIO_BREAK) {
161                 // write data
162                 if(!(motor_on && !wrga)) {
163                         return;
164                 }
165                 if(id == QUICKDISK_SIO_DATA) {
166                         if(first_data) {
167                                 // write sync chars at the top of message
168                                 WRITE_BUFFER(DATA_SYNC);
169                                 WRITE_BUFFER(DATA_SYNC);
170                                 first_data = false;
171                         }
172                         WRITE_BUFFER(data);
173                         write_ptr = buffer_ptr;
174                 } else if(id == QUICKDISK_SIO_BREAK) {
175                         write_crc();
176                         WRITE_BUFFER(DATA_BREAK);
177                         first_data = true;
178                         write_ptr = 0;
179                 }
180                 accessed = true;
181                 
182                 if(buffer_ptr < QUICKDISK_BUFFER_SIZE) {
183                         REGISTER_END_EVENT();
184                 } else {
185                         CANCEL_END_EVENT();
186                         end_of_disk();
187                 }
188         }
189 }
190
191 uint32_t QUICKDISK::read_signal(int ch)
192 {
193         // access lamp signal
194         if(accessed) {
195                 accessed = false;
196                 return 1;
197         }
198         return 0;
199 }
200
201 void QUICKDISK::event_callback(int event_id, int err)
202 {
203         if(event_id == EVENT_RESTORE) {
204                 // reached to home position
205                 restore_id = -1;
206                 restore();
207         } else if(event_id == EVENT_END) {
208                 // reached to end of disk
209                 end_id = -1;
210                 end_of_disk();
211         }
212 }
213
214 void QUICKDISK::restore()
215 {
216         // reached to home position
217         set_home(false);
218         buffer_ptr = 0;
219         first_data = send_break = true;
220         
221         // start to send
222         send_data();
223 }
224
225 void QUICKDISK::send_data()
226 {
227         if(!(motor_on && wrga) || restore_id != -1) {
228                 return;
229         }
230 retry:
231         if(buffer_ptr < QUICKDISK_BUFFER_SIZE && buffer[buffer_ptr] != DATA_EMPTY) {
232                 if(buffer[buffer_ptr] == DATA_BREAK) {
233                         // send break signal
234                         if(send_break) {
235                                 d_sio->write_signal(SIG_Z80SIO_BREAK_CH0, 1, 1);
236                                 send_break = false;
237                         }
238                         // wait until sio enters hunt/sync phase
239                         if(!sync) {
240                                 return;
241                         }
242                         buffer_ptr++;
243                         goto retry;
244                 }
245                 // send data
246                 d_sio->write_signal(SIG_Z80SIO_RECV_CH0, buffer[buffer_ptr++], 0xff);
247                 send_break = true;
248                 accessed = true;
249                 REGISTER_END_EVENT();
250         } else {
251                 // reached to end of disk
252                 CANCEL_END_EVENT();
253                 end_of_disk();
254         }
255 }
256
257 void QUICKDISK::write_crc()
258 {
259         if(!wrga && write_ptr != 0) {
260                 buffer_ptr = write_ptr;
261                 
262                 WRITE_BUFFER(DATA_CRC);
263                 WRITE_BUFFER(DATA_CRC);
264                 WRITE_BUFFER(DATA_SYNC);
265                 WRITE_BUFFER(DATA_SYNC);
266                 // don't increment pointer !!!
267                 WRITE_BUFFER(DATA_BREAK);
268                 buffer_ptr--;
269         }
270         write_ptr = 0;
271 }
272
273 void QUICKDISK::end_of_disk()
274 {
275         // write crc
276         write_crc();
277         
278         // reached to end of disk
279         if(mton || !wrga) {
280                 motor_on = false;
281         } else {
282                 REGISTER_RESTORE_EVENT();
283         }
284         set_home(true);
285 }
286
287 void QUICKDISK::set_insert(bool val)
288 {
289         // L=inserted
290         d_sio->write_signal(SIG_Z80SIO_DCD_CH0, val ? 0 : 1, 1);
291         insert = val;
292 }
293
294 void QUICKDISK::set_protect(bool val)
295 {
296         // H=protected
297         d_sio->write_signal(SIG_Z80SIO_CTS_CH0, val ? 1 : 0, 1);
298         protect = val;
299 }
300
301 void QUICKDISK::set_home(bool val)
302 {
303         if(home != val) {
304                 d_sio->write_signal(SIG_Z80SIO_DCD_CH1, val ? 1 : 0, 1);
305                 home = val;
306         }
307 }
308
309 void QUICKDISK::open_disk(const _TCHAR* path)
310 {
311         // check current disk image
312         if(insert) {
313                 if(_tcsicmp(file_path, path) == 0) {
314                         return;
315                 }
316                 // close current disk
317                 close_disk();
318         }
319         memset(buffer, 0, sizeof(buffer));
320         
321         // load disk image
322         FILEIO* fio = new FILEIO();
323         if(fio->Fopen(path, FILEIO_READ_BINARY)) {
324                 my_tcscpy_s(file_path, _MAX_PATH, path);
325                 
326                 // clear buffer
327                 for(int i = 0; i < QUICKDISK_BUFFER_SIZE; i++) {
328                         buffer[i] = DATA_EMPTY;
329                 }
330                 buffer_ptr = 0;
331                 modified = false;
332                 
333                 // check extension
334                 if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {
335                         // load mzt file
336                         fio->Fseek(0, FILEIO_SEEK_END);
337                         int remain = fio->Ftell();
338                         fio->Fseek(0, FILEIO_SEEK_SET);
339                         
340                         int num_block = 0;
341                         int block_num_ptr = 0;
342                         
343                         // create block file
344                         buffer[buffer_ptr++] = DATA_BREAK;
345                         buffer[buffer_ptr++] = DATA_SYNC;
346                         buffer[buffer_ptr++] = DATA_SYNC;
347                         buffer[buffer_ptr++] = DATA_MARK;
348                         block_num_ptr = buffer_ptr;
349                         buffer[buffer_ptr++] = 0; // block number
350                         buffer[buffer_ptr++] = DATA_CRC;
351                         buffer[buffer_ptr++] = DATA_CRC;
352                         buffer[buffer_ptr++] = DATA_SYNC;
353                         buffer[buffer_ptr++] = DATA_SYNC;
354                         buffer[buffer_ptr++] = DATA_BREAK;
355                         
356                         while(remain >= MZT_HEADER_SIZE) {
357                                 // load header
358                                 uint8_t header[MZT_HEADER_SIZE], ram[0x20000];
359                                 fio->Fread(header, MZT_HEADER_SIZE, 1);
360                                 remain -= MZT_HEADER_SIZE;
361                                 
362                                 // load data
363                                 int size = header[0x12] | (header[0x13] << 8);
364                                 int offs = header[0x14] | (header[0x15] << 8);
365                                 memset(ram, 0, sizeof(ram));
366                                 fio->Fread(ram + offs, size, 1);
367                                 remain -= size;
368 #if 0
369                                 // apply mz700win patch
370                                 if(header[0x40] == 'P' && header[0x41] == 'A' && header[0x42] == 'T' && header[0x43] == ':') {
371                                         int patch_ofs = 0x44;
372                                         for(; patch_ofs < 0x80; ) {
373                                                 uint16_t patch_addr = header[patch_ofs] | (header[patch_ofs + 1] << 8);
374                                                 patch_ofs += 2;
375                                                 if(patch_addr == 0xffff) {
376                                                         break;
377                                                 }
378                                                 int patch_len = header[patch_ofs++];
379                                                 for(int i = 0; i < patch_len; i++) {
380                                                         ram[patch_addr + i] = header[patch_ofs++];
381                                                 }
382                                         }
383                                         // clear patch data
384                                         for(int i = 0x40; i < patch_ofs; i++) {
385                                                 header[i] = 0;
386                                         }
387                                 }
388 #endif
389                                 // copy header
390                                 buffer[block_num_ptr] = ++num_block;
391                                 
392                                 buffer[buffer_ptr++] = DATA_SYNC;
393                                 buffer[buffer_ptr++] = DATA_SYNC;
394                                 buffer[buffer_ptr++] = DATA_MARK;
395                                 buffer[buffer_ptr++] = HEADER_BLOCK_ID;
396                                 buffer[buffer_ptr++] = HEADER_SIZE;
397                                 buffer[buffer_ptr++] = 0;
398                                 buffer[buffer_ptr++] = header[0];       // attribute
399                                 for(int i = 0; i < 17; i++) {
400                                         buffer[buffer_ptr++] = header[i + 1]; // file name
401                                 }
402                                 buffer[buffer_ptr++] = header[0x3e];    // lock
403                                 buffer[buffer_ptr++] = header[0x3f];    // secret
404                                 buffer[buffer_ptr++] = header[0x12];    // file size
405                                 buffer[buffer_ptr++] = header[0x13];
406                                 buffer[buffer_ptr++] = header[0x14];    // load addr
407                                 buffer[buffer_ptr++] = header[0x15];
408                                 buffer[buffer_ptr++] = header[0x16];    // exec addr
409                                 buffer[buffer_ptr++] = header[0x17];
410                                 for(int i = 26; i < HEADER_SIZE; i++) {
411                                         buffer[buffer_ptr++] = 0;       // comment
412                                 }
413                                 buffer[buffer_ptr++] = DATA_CRC;
414                                 buffer[buffer_ptr++] = DATA_CRC;
415                                 buffer[buffer_ptr++] = DATA_SYNC;
416                                 buffer[buffer_ptr++] = DATA_SYNC;
417                                 buffer[buffer_ptr++] = DATA_BREAK;
418                                 
419                                 // copy data
420                                 buffer[block_num_ptr] = ++num_block;
421                                 
422                                 buffer[buffer_ptr++] = DATA_SYNC;
423                                 buffer[buffer_ptr++] = DATA_SYNC;
424                                 buffer[buffer_ptr++] = DATA_MARK;
425                                 buffer[buffer_ptr++] = DATA_BLOCK_ID;
426                                 buffer[buffer_ptr++] = (uint8_t)(size & 0xff);
427                                 buffer[buffer_ptr++] = (uint8_t)(size >> 8);
428                                 for(int i = 0; i < size; i++) {
429                                         buffer[buffer_ptr++] = ram[offs + i];
430                                 }
431                                 buffer[buffer_ptr++] = DATA_CRC;
432                                 buffer[buffer_ptr++] = DATA_CRC;
433                                 buffer[buffer_ptr++] = DATA_SYNC;
434                                 buffer[buffer_ptr++] = DATA_SYNC;
435                                 buffer[buffer_ptr++] = DATA_BREAK;
436                         }
437                 } else {
438                         // check header
439                         uint8_t header[16];
440                         fio->Fread(header, sizeof(header), 1);
441                         if(memcmp(header, "-QD format-", 11) != 0) {
442                                 fio->Fseek(0, FILEIO_SEEK_SET);
443                         }
444                         
445                         // load raw file
446                         bool in_gap = true;
447                         int sync_top_ptr = 0, sync_num = 0, sync_num_prev = 0, data;
448                         
449                         buffer[buffer_ptr++] = DATA_BREAK;
450                         
451                         while((data = fio->Fgetc()) != EOF) {
452                                 if(data == DATA_SYNC) {
453                                         if(sync_num == 0) {
454                                                 sync_top_ptr = buffer_ptr;
455                                         }
456                                         sync_num++;
457                                 } else {
458                                         sync_num_prev = sync_num;
459                                         sync_num = 0;
460                                 }
461                                 if(in_gap) {
462                                         if(sync_num_prev >= 4 && sync_num == 0) {
463                                                 buffer[buffer_ptr++] = DATA_SYNC;
464                                                 buffer[buffer_ptr++] = DATA_SYNC;
465                                                 buffer[buffer_ptr++] = data;
466                                                 in_gap = false;
467                                         }
468                                 } else {
469                                         if(sync_num_prev >= 4 && sync_num == 0 && data == 0x00) {
470                                                 buffer_ptr = sync_top_ptr;
471                                                 buffer[buffer_ptr++] = DATA_SYNC;
472                                                 buffer[buffer_ptr++] = DATA_SYNC;
473                                                 buffer[buffer_ptr++] = DATA_BREAK;
474                                                 in_gap = true;
475                                         } else {
476                                                 buffer[buffer_ptr++] = data;
477                                         }
478                                 }
479                         }
480                 }
481                 set_insert(true);
482                 set_protect(FILEIO::IsFileProtected(path));
483                 set_home(true);
484                 
485                 fio->Fclose();
486         }
487         delete fio;
488 }
489
490 void QUICKDISK::close_disk()
491 {
492         release_disk();
493         set_insert(false);
494         set_protect(false);
495         set_home(true);
496         
497         // cancel all events
498         CANCEL_RESTORE_EVENT();
499         CANCEL_END_EVENT();
500 }
501
502 void QUICKDISK::release_disk()
503 {
504         if(insert && !protect && modified) {
505                 // check extension
506                 _TCHAR file_path_tmp[_MAX_PATH];
507                 if(check_file_extension(file_path, _T(".mzt")) || check_file_extension(file_path, _T(".q20"))) {
508                         my_tcscpy_s(file_path_tmp, _MAX_PATH, file_path);
509                 } else {
510                         my_stprintf_s(file_path_tmp, _MAX_PATH, _T("%s.mzt"), get_file_path_without_extensiton(file_path));
511                 }
512                 // save blocks as mzt file
513                 FILEIO* fio = new FILEIO();
514                 if(!fio->Fopen(file_path_tmp, FILEIO_WRITE_BINARY)) {
515                         fio->Fopen(create_local_path(_T("temporary_saved_quick_disk.mzt")), FILEIO_WRITE_BINARY);
516                 }
517                 if(fio->IsOpened()) {
518                         int block_num = buffer[4];
519                         buffer_ptr = 10;
520                         
521                         for(int i = 0; i < block_num; i++) {
522                                 if(buffer[buffer_ptr] == DATA_EMPTY) {
523                                         break;
524                                 }
525                                 int id = buffer[buffer_ptr + 3] & 3;
526                                 int size = buffer[buffer_ptr + 4] | (buffer[buffer_ptr + 5] << 8);
527                                 buffer_ptr += 6;
528                                 
529                                 if(id == HEADER_BLOCK_ID) {
530                                         // create mzt header
531                                         uint8_t header[MZT_HEADER_SIZE];
532                                         memset(header, 0, sizeof(header));
533                                         
534                                         header[0x00] = (uint8_t)buffer[buffer_ptr + 0]; // attribute
535                                         for(int i = 1; i <= 17; i++) {
536                                                 header[i] = (uint8_t)buffer[buffer_ptr + i];    // file name
537                                         }
538                                         header[0x3e] = (uint8_t)buffer[buffer_ptr + 18];        // lock
539                                         header[0x3f] = (uint8_t)buffer[buffer_ptr + 19];        // lock
540                                         header[0x12] = (uint8_t)buffer[buffer_ptr + 20];        // file size
541                                         header[0x13] = (uint8_t)buffer[buffer_ptr + 21];
542                                         header[0x14] = (uint8_t)buffer[buffer_ptr + 22];        // load addr
543                                         header[0x15] = (uint8_t)buffer[buffer_ptr + 23];
544                                         header[0x16] = (uint8_t)buffer[buffer_ptr + 24];        // exec addr
545                                         header[0x17] = (uint8_t)buffer[buffer_ptr + 25];
546                                         fio->Fwrite(header, MZT_HEADER_SIZE, 1);
547                                 } else {
548                                         // data
549                                         for(int i = 0; i < size; i++) {
550                                                 fio->Fputc(buffer[buffer_ptr + i]);
551                                         }
552                                 }
553                                 buffer_ptr += size + 5;
554                         }
555                         fio->Fclose();
556                 }
557                 delete fio;
558         }
559 }
560
561 #define STATE_VERSION   1
562
563 bool QUICKDISK::process_state(FILEIO* state_fio, bool loading)
564 {
565         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
566                 return false;
567         }
568         if(!state_fio->StateCheckInt32(this_device_id)) {
569                 return false;
570         }
571         state_fio->StateBuffer(file_path, sizeof(file_path), 1);
572         state_fio->StateBool(insert);
573         state_fio->StateBool(protect);
574         state_fio->StateBool(home);
575         state_fio->StateBool(modified);
576         state_fio->StateBool(accessed);
577         //state_fio->StateBuffer(buffer, sizeof(buffer), 1);
578         for(int i = 0; i < QUICKDISK_BUFFER_SIZE; i++) {
579                 state_fio->StateUint16(buffer[i]);
580         }
581         state_fio->StateInt32(buffer_ptr);
582         state_fio->StateInt32(write_ptr);
583         state_fio->StateBool(first_data);
584         state_fio->StateBool(send_break);
585         state_fio->StateBool(wrga);
586         state_fio->StateBool(mton);
587         state_fio->StateBool(sync);
588         state_fio->StateBool(motor_on);
589         state_fio->StateInt32(restore_id);
590         state_fio->StateInt32(end_id);
591         return true;
592 }