OSDN Git Service

[VM][COMMON_VM] Include IO:: class to common_vm.
[csp-qt/common_source_project-fm7.git] / source / src / vm / t3444a.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2015.09.03-
6
7         [ T3444A / T3444M ]
8 */
9
10 #include "t3444a.h"
11 #include "disk.h"
12 #include "noise.h"
13
14 // TODO: check status in data lost
15 #define FDC_STA_FDC_READY       0x08    // 1=Ready, 0=Busy
16 #define FDC_STA_CMD_ERROR       0x06    // Execute command other than Seek to Zero when the drive is not ready, or execute write command when the disk is write protected
17 #define FDC_STA_ID_MISSING      0x02    // ID field is missing
18 #define FDC_STA_ID_ERROR        0x05    // Track address in ID field is 0xff (defective track)
19 #define FDC_STA_SEEK_ERROR      0x07    // Seek error
20 #define FDC_STA_DATA_ERROR      0x01    // Find error in data field
21 #define FDC_STA_LAST_SECTOR     0x03    // Terminate read/write command at the last sector in the track when tnd signal is active (low)
22 #define FDC_STA_SUCCESS         0x00    // Command is successfully executed
23 #define FDC_STA_SUCCESS_DDM     0x04    // Command is successfully executed and detect the deleted data address mark
24
25 #define FDC_CMD_SEEK_ZERO       0x00    // Seek to Zero
26 #define FDC_CMD_SEEK            0x03    // Seek
27 #define FDC_CMD_WRITE_ID        0x08    // Write Index/ID
28 #define FDC_CMD_SEEK_WRITE_ID   0x0a    // Seek and Write Index/ID
29 #define FDC_CMD_READ            0x05    // Read Data
30 #define FDC_CMD_SEEK_READ       0x07    // Seek and Read Data
31 #define FDC_CMD_WRITE           0x09    // Write Data
32 #define FDC_CMD_WRITE_DDM       0x0d    // Write Data with Deleted Data Address Mark
33 #define FDC_CMD_SEEK_WRITE      0x0b    // Seek and Write Data
34 #define FDC_CMD_SEEK_WRITE_DDM  0x0f    // Seek and Write Data with Deleted Data Address Mark
35 #define FDC_CMD_SENCE_DRV_STAT  0x01    // Sence Drive Status
36
37 #define EVENT_SEEK              0
38 #define EVENT_SEARCH            1
39 #define EVENT_RQM               2
40 #define EVENT_LOST              3
41 #define EVENT_TND               4
42
43 void T3444A::cancel_my_event(int event)
44 {
45         if(register_id[event] != -1) {
46                 cancel_event(this, register_id[event]);
47                 register_id[event] = -1;
48         }
49 }
50
51 void T3444A::register_my_event(int event, double usec)
52 {
53         cancel_my_event(event);
54         register_event(this, event, usec, false, &register_id[event]);
55 }
56
57 void T3444A::register_seek_event()
58 {
59         cancel_my_event(EVENT_SEEK);
60         if(fdc[drvreg].track == seektrk) {
61                 register_event(this, EVENT_SEEK, 1, false, &register_id[EVENT_SEEK]);
62         } else {
63                 register_event(this, EVENT_SEEK, timerflag ? 40000 : 25000, false, &register_id[EVENT_SEEK]);
64         }
65 }
66
67 void T3444A::register_rqm_event(int bytes)
68 {
69         double usec = disk[drvreg]->get_usec_per_bytes(bytes) - get_passed_usec(prev_rqm_clock);
70         if(usec < 4) {
71                 usec = 4;
72         }
73         cancel_my_event(EVENT_RQM);
74         register_event(this, EVENT_RQM, usec, false, &register_id[EVENT_RQM]);
75 }
76
77 void T3444A::register_lost_event(int bytes)
78 {
79         cancel_my_event(EVENT_LOST);
80         register_event(this, EVENT_LOST, disk[drvreg]->get_usec_per_bytes(bytes), false, &register_id[EVENT_LOST]);
81 }
82
83 void T3444A::initialize()
84 {
85         DEVICE::initialize();
86         _max_drive = osd->get_feature_int_value(_T("MAX_DRIVE"));
87         if((_max_drive <= 0) || (_max_drive >= 4)) _max_drive = 4;
88         _has_t3444m = osd->check_feature(_T("HAS_T3444M"));
89         //_fdc_debug_log = osd->check_feature(_T("_FDC_DEBUG_LOG"));
90         _fdc_debug_log = config.special_debug_fdc;
91         
92         _sectors_in_track = (_has_t3444m) ? 16 : 26;
93         // initialize d88 handler
94         for(int i = 0; i < 4; i++) {
95                 disk[i] = new DISK(emu);
96                 disk[i]->set_device_name(_T("%s/Disk #%d"), this_device_name, i + 1);
97         }
98         
99         // initialize noise
100         if(d_noise_seek != NULL) {
101                 d_noise_seek->set_device_name(_T("Noise Player (FDD Seek)"));
102                 if(!d_noise_seek->load_wav_file(_T("FDDSEEK.WAV"))) {
103                         if(!d_noise_seek->load_wav_file(_T("FDDSEEK1.WAV"))) {
104                                 d_noise_seek->load_wav_file(_T("SEEK.WAV"));
105                         }
106                 }
107                 d_noise_seek->set_mute(!config.sound_noise_fdd);
108         }
109         if(d_noise_head_down != NULL) {
110                 d_noise_head_down->set_device_name(_T("Noise Player (FDD Head Load)"));
111                 d_noise_head_down->load_wav_file(_T("HEADDOWN.WAV"));
112                 d_noise_head_down->set_mute(!config.sound_noise_fdd);
113         }
114         if(d_noise_head_up != NULL) {
115                 d_noise_head_up->set_device_name(_T("Noise Player (FDD Head Unload)"));
116                 d_noise_head_up->load_wav_file(_T("HEADUP.WAV"));
117                 d_noise_head_up->set_mute(!config.sound_noise_fdd);
118         }
119         
120         // initialize timing
121         memset(fdc, 0, sizeof(fdc));
122         
123         // initialize fdc
124         seektrk = 0;
125         status = FDC_STA_FDC_READY | FDC_STA_SUCCESS;
126         cmdreg = trkreg = secreg = datareg = 0;
127         drvreg = sidereg = 0;
128         timerflag = false;
129         prev_rqm_clock = 0;
130 }
131
132 void T3444A::release()
133 {
134         // release d88 handler
135         for(int i = 0; i < 4; i++) {
136                 if(disk[i]) {
137                         disk[i]->close();
138                         delete disk[i];
139                 }
140         }
141 }
142
143 void T3444A::reset()
144 {
145         for(int i = 0; i < 4; i++) {
146                 fdc[i].track = 0;
147                 fdc[i].index = 0;
148                 fdc[i].access = false;
149         }
150         for(long unsigned int i = 0; i < array_length(register_id); i++) {
151                 register_id[i] = -1;
152         }
153         now_search = false;
154         set_rqm(false);
155 }
156
157 void T3444A::write_io8(uint32_t addr, uint32_t data)
158 {
159         switch(addr & 3) {
160         case 0:
161                 // command reg
162                 cmdreg = data & 0x0f;
163                 process_cmd();
164                 break;
165         case 1:
166                 // track reg
167                 trkreg = data & 0x7f;
168                 timerflag = ((data & 0x80) != 0);
169                 break;
170         case 2:
171                 // sector reg
172                 secreg = data & 0x7f;
173 //#ifdef HAS_T3444M
174                 if(_has_t3444m) sidereg = (data >> 7) & 1;
175 //#endif
176                 break;
177         case 3:
178                 // data reg
179                 datareg = data;
180                 if(motor_on && rqm && !now_search) {
181                         if(cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM) {
182                                 // write data
183                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
184                                         disk[drvreg]->sector[fdc[drvreg].index++] = datareg;
185                                         disk[drvreg]->set_deleted(cmdreg == FDC_CMD_WRITE_DDM);
186                                         fdc[drvreg].access = true;
187                                 }
188                                 set_rqm(false);
189                                 cancel_my_event(EVENT_LOST);
190                                 
191                                 if(fdc[drvreg].index >= disk[drvreg]->sector_size.sd) {
192 //#ifdef _FDC_DEBUG_LOG
193                                         if(_fdc_debug_log) this->out_debug_log(_T("FDC\tWRITE DATA FINISHED\n"));
194 //#endif
195                                         // 2S: 300rpm, 3100bytes/track -> 0.0155bytes/us
196                                         register_my_event(EVENT_TND, 100); // 0.0155bytes/us * 100us = 1.55bytes < GAP3
197                                 } else {
198                                         if(fdc[drvreg].index == 1) {
199                                                 register_rqm_event(fdc[drvreg].bytes_before_2nd_rqm);
200                                         } else {
201                                                 register_rqm_event(1);
202                                         }
203                                 }
204                         } else if(cmdreg == FDC_CMD_WRITE_ID) {
205                                 // write index/id
206                                 if(fdc[drvreg].index < _sectors_in_track * 4) {
207                                         sector_id[fdc[drvreg].index++] = datareg;
208                                 }
209                                 set_rqm(false);
210                                 cancel_my_event(EVENT_LOST);
211                                 
212                                 if(fdc[drvreg].index >= _sectors_in_track * 4) {
213                                         // format in single-density
214                                         bool drive_mfm = disk[drvreg]->drive_mfm;
215                                         disk[drvreg]->drive_mfm = false;
216                                         disk[drvreg]->format_track(fdc[drvreg].track, sidereg);
217                                         disk[drvreg]->drive_mfm = drive_mfm;
218                                         for(int i = 0; i < _sectors_in_track; i++) {
219                                                 disk[drvreg]->insert_sector(sector_id[i * 4], sector_id[i * 4 + 1], sector_id[i * 4 + 2], sector_id[i * 4 + 3], false, false, 0xff, 128);
220                                         }
221                                         status |= FDC_STA_FDC_READY;
222                                 } else {
223                                         register_rqm_event(1);
224                                 }
225                         }
226                 }
227                 break;
228         }
229 }
230
231 uint32_t T3444A::read_io8(uint32_t addr)
232 {
233         switch(addr & 3) {
234         case 0:
235                 // status reg
236 //#ifdef _FDC_DEBUG_LOG
237                 if(_fdc_debug_log) this->out_debug_log(_T("FDC\tSTATUS=%02x\n"),status);
238 //#endif
239                 return status;
240         case 3:
241                 // data reg
242                 if(motor_on && rqm && !now_search) {
243                         if(cmdreg == FDC_CMD_READ) {
244                                 // read data
245                                 if(fdc[drvreg].index < disk[drvreg]->sector_size.sd) {
246                                         datareg = disk[drvreg]->sector[fdc[drvreg].index++];
247                                         fdc[drvreg].access = true;
248                                 }
249                                 set_rqm(false);
250                                 cancel_my_event(EVENT_LOST);
251                                 
252                                 if(fdc[drvreg].index >= disk[drvreg]->sector_size.sd) {
253 //#ifdef _FDC_DEBUG_LOG
254                                         if(_fdc_debug_log) this->out_debug_log(_T("FDC\tREAD DATA FINISHED\n"));
255 //#endif
256 //                                      if(status == FDC_STA_DATA_ERROR) {
257 //                                              status |= FDC_STA_FDC_READY;
258 //                                      } else {
259                                                 register_my_event(EVENT_TND, 100);
260 //                                      }
261                                 } else {
262                                         register_rqm_event(1);
263                                 }
264                         }
265                 }
266                 return datareg;
267         }
268         return 0xff;
269 }
270
271 void T3444A::write_dma_io8(uint32_t addr, uint32_t data)
272 {
273         write_io8(3, data);
274 }
275
276 uint32_t T3444A::read_dma_io8(uint32_t addr)
277 {
278         return read_io8(3);
279 }
280
281 void T3444A::write_signal(int id, uint32_t data, uint32_t mask)
282 {
283         if(id == SIG_T3444A_DRIVE) {
284                 if(data & 4) {
285                         drvreg = data & 3;
286                 }
287         } else if(id == SIG_T3444A_TND) {
288                 tnd = ((data & mask) != 0);
289         } else if(id == SIG_T3444A_MOTOR) {
290                 motor_on = ((data & mask) != 0);
291         }
292 }
293
294 uint32_t T3444A::read_signal(int ch)
295 {
296         if(ch == SIG_T3444A_DRDY) {
297                 return (disk[drvreg]->inserted && motor_on) ? 1 : 0;
298         } else if(ch == SIG_T3444A_CRDY) {
299                 return (status & FDC_STA_FDC_READY) ? 1 : 0;
300         } else if(ch == SIG_T3444A_RQM) {
301                 // this is negative signal
302                 return rqm ? 1 : 0;
303         }
304         
305         // get access status
306         uint32_t stat = 0;
307         for(int i = 0; i < 4; i++) {
308                 if(fdc[i].access) {
309                         stat |= 1 << i;
310                 }
311                 fdc[i].access = false;
312         }
313         if(now_search) {
314                 stat |= 1 << drvreg;
315         }
316         return stat;
317 }
318
319 void T3444A::event_callback(int event_id, int err)
320 {
321         register_id[event_id] = -1;
322         
323         switch(event_id) {
324         case EVENT_SEEK:
325                 if(seektrk > fdc[drvreg].track) {
326                         fdc[drvreg].track++;
327                         if(d_noise_seek != NULL) d_noise_seek->play();
328                 } else if(seektrk < fdc[drvreg].track) {
329                         fdc[drvreg].track--;
330                         if(d_noise_seek != NULL) d_noise_seek->play();
331                 }
332                 if(seektrk != fdc[drvreg].track) {
333                         register_seek_event();
334                         break;
335                 }
336                 if(cmdreg == FDC_CMD_SEEK_READ) {
337                         cmdreg = FDC_CMD_READ;
338                         process_cmd();
339                 } else if(cmdreg == FDC_CMD_SEEK_WRITE) {
340                         cmdreg = FDC_CMD_WRITE;
341                         process_cmd();
342                 } else if(cmdreg == FDC_CMD_SEEK_WRITE_DDM) {
343                         cmdreg = FDC_CMD_WRITE_DDM;
344                         process_cmd();
345                 } else {
346                         status |= FDC_STA_FDC_READY;
347                 }
348                 break;
349         case EVENT_SEARCH:
350                 if(status == FDC_STA_SUCCESS || status == FDC_STA_SUCCESS_DDM || status == FDC_STA_DATA_ERROR) {
351                         if(cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM) {
352                                 register_lost_event(8);
353                         } else if(cmdreg == FDC_CMD_SEEK_WRITE_ID) {
354                                 register_lost_event(1); // is this okay ???
355                         } else {
356                                 register_lost_event(1);
357                         }
358                         fdc[drvreg].cur_position = fdc[drvreg].next_trans_position;
359                         fdc[drvreg].prev_clock = prev_rqm_clock = get_current_clock();
360                         set_rqm(true);
361                 } else {
362                         status |= FDC_STA_FDC_READY;
363                 }
364                 now_search = false;
365                 break;
366         case EVENT_RQM:
367                 if(!(status & FDC_STA_FDC_READY)) {
368                         if((cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM) && fdc[drvreg].index == 0) {
369                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + fdc[drvreg].bytes_before_2nd_rqm) % disk[drvreg]->get_track_size();
370                         } else {
371                                 fdc[drvreg].cur_position = (fdc[drvreg].cur_position + 1) % disk[drvreg]->get_track_size();
372                         }
373                         fdc[drvreg].prev_clock = prev_rqm_clock = get_current_clock();
374                         set_rqm(true);
375                         if(cmdreg == FDC_CMD_SEEK_WRITE_ID) {
376                                 register_lost_event(1); // is this okay ???
377                         } else {
378                                 register_lost_event(1);
379                         }
380                 }
381                 break;
382         case EVENT_LOST:
383                 status |= FDC_STA_FDC_READY;
384                 break;
385         case EVENT_TND:
386                 if(!tnd) {
387                         if(secreg < _sectors_in_track) {
388                                 secreg++;
389 //#ifdef _FDC_DEBUG_LOG
390                                 if(_fdc_debug_log) this->out_debug_log(_T("FDC\tTND AND CONTINUE SEC=%d\n"), secreg);
391 //#endif
392                                 cmd_read_write();
393                         } else {
394 //                              secreg = 1;
395 //#ifdef _FDC_DEBUG_LOG
396                                 if(_fdc_debug_log) this->out_debug_log(_T("FDC\tTND BUT TERMINATED SEC=%d\n"), secreg);
397 //#endif
398                                 status = FDC_STA_FDC_READY | FDC_STA_LAST_SECTOR;
399                         }
400                 } else {
401                         status |= FDC_STA_FDC_READY;
402                 }
403                 break;
404         }
405 }
406
407 // ----------------------------------------------------------------------------
408 // command
409 // ----------------------------------------------------------------------------
410
411 void T3444A::process_cmd()
412 {
413 //#ifdef _FDC_DEBUG_LOG
414         static const _TCHAR *cmdstr[0x10] = {
415                 _T("Seek to Zero"),
416                 _T("Sence Drive Status"),
417                 _T("Unknown"),
418                 _T("Seek"),
419                 _T("Unknown"),
420                 _T("Read Data"),
421                 _T("Unknown"),
422                 _T("Seek and Read Data"),
423                 _T("Write Index/ID"),
424                 _T("Write Data"),
425                 _T("Seek and Write Index/ID"),
426                 _T("Seek and Write Data"),
427                 _T("Unknown"),
428                 _T("Write Data with Deleted Data Address Mark"),
429                 _T("Unknown"),
430                 _T("Seek and Write Data with Deleted Data Address Mark"),
431         };
432         if(_fdc_debug_log)  {
433                 //if(cmdreg == cmdreg) {
434                         this->out_debug_log(_T("FDC\tCMD=%2xh (%s) DATA=%2xh DRV=%d TRK=%3d SIDE=%d SEC=%2d\n"), cmdreg, cmdstr[cmdreg], datareg, drvreg, trkreg, sidereg, secreg);
435                 //}
436         }
437 //#endif
438         status = 0; // FDC is busy
439         
440         switch(cmdreg) {
441         case FDC_CMD_SEEK_ZERO:
442                 cmd_seek_zero();
443                 update_head_flag(drvreg, false);
444                 break;
445         case FDC_CMD_SEEK:
446         case FDC_CMD_SEEK_READ:
447         case FDC_CMD_SEEK_WRITE:
448         case FDC_CMD_SEEK_WRITE_DDM:
449         case FDC_CMD_SEEK_WRITE_ID:
450                 cmd_seek();
451                 update_head_flag(drvreg, false);
452                 break;
453         case FDC_CMD_READ:
454         case FDC_CMD_WRITE:
455         case FDC_CMD_WRITE_DDM:
456                 cmd_read_write();
457                 update_head_flag(drvreg, true);
458                 break;
459         case FDC_CMD_WRITE_ID:
460                 cmd_write_id();
461                 update_head_flag(drvreg, true);
462                 break;
463         case FDC_CMD_SENCE_DRV_STAT:
464                 cmd_sence();
465                 break;
466         default:
467                 status = FDC_STA_FDC_READY | FDC_STA_CMD_ERROR; // is this okay ???
468                 break;
469         }
470 }
471
472 void T3444A::cmd_seek_zero()
473 {
474         if(!disk[drvreg]->inserted || !motor_on) {
475 //              status = FDC_STA_FDC_READY | FDC_STA_SEEK_ERROR;
476                 status = FDC_STA_FDC_READY | FDC_STA_SUCCESS;
477                 seektrk = trkreg = 0;
478         } else {
479                 status = FDC_STA_SUCCESS;
480                 seektrk = trkreg = 0;
481                 register_seek_event();
482         }
483 }
484
485 void T3444A::cmd_seek()
486 {
487         if(!disk[drvreg]->inserted || !motor_on) {
488                 status = FDC_STA_FDC_READY | FDC_STA_CMD_ERROR;
489         } else if(trkreg > 34) {
490                 status = FDC_STA_FDC_READY | FDC_STA_SEEK_ERROR;
491         } else {
492                 status = FDC_STA_SUCCESS;
493                 seektrk = trkreg;
494                 register_seek_event();
495         }
496 }
497
498 void T3444A::cmd_read_write()
499 {
500         if(!((status = search_sector()) & FDC_STA_FDC_READY)) {
501                 double time;
502                 if(status == FDC_STA_SUCCESS || status == FDC_STA_SUCCESS_DDM || status == FDC_STA_DATA_ERROR) {
503                         time = get_usec_to_start_trans();
504                 } else {
505                         time = get_usec_to_detect_index_hole(3);
506                 }
507                 now_search = true;
508                 register_my_event(EVENT_SEARCH, time);
509                 cancel_my_event(EVENT_LOST);
510         }
511 }
512
513 void T3444A::cmd_write_id()
514 {
515         if(!disk[drvreg]->inserted || !motor_on || disk[drvreg]->write_protected) {
516                 status =  FDC_STA_FDC_READY | FDC_STA_CMD_ERROR;
517         } else {
518                 // raise first rqm soon
519                 fdc[drvreg].next_trans_position = (get_cur_position() + 1) % disk[drvreg]->get_track_size();
520                 fdc[drvreg].index = 0;
521                 status = FDC_STA_SUCCESS;
522                 now_search = true;
523                 register_my_event(EVENT_SEARCH, disk[drvreg]->get_usec_per_bytes(1));
524                 cancel_my_event(EVENT_LOST);
525         }
526 }
527
528 void T3444A::cmd_sence()
529 {
530         if(!disk[drvreg]->inserted || !motor_on) {
531                 status = FDC_STA_FDC_READY | FDC_STA_CMD_ERROR;
532         } else {
533                 trkreg = fdc[drvreg].track;
534                 status = FDC_STA_FDC_READY | FDC_STA_SUCCESS;
535         }
536 }
537
538 void T3444A::update_head_flag(int drv, bool head_load)
539 {
540         if(fdc[drv].head_load != head_load) {
541                 if(head_load) {
542                         if(d_noise_head_down != NULL) d_noise_head_down->play();
543                 } else {
544                         if(d_noise_head_up != NULL) d_noise_head_up->play();
545                 }
546                 fdc[drv].head_load = head_load;
547         }
548 }
549
550 // ----------------------------------------------------------------------------
551 // media handler
552 // ----------------------------------------------------------------------------
553
554 uint8_t T3444A::search_sector()
555 {
556         // drive not ready
557         if(!disk[drvreg]->inserted || !motor_on) {
558                 return FDC_STA_FDC_READY | FDC_STA_CMD_ERROR;
559         }
560         
561         // write protect
562         if(cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM || cmdreg == FDC_CMD_WRITE_ID) {
563                 if(disk[drvreg]->write_protected) {
564                         return FDC_STA_FDC_READY | FDC_STA_CMD_ERROR;
565                 }
566         }
567         
568         // get track
569         if(!disk[drvreg]->get_track(fdc[drvreg].track, sidereg)) {
570                 return FDC_STA_ID_MISSING;
571         }
572         
573         // get current position
574         int sector_num = disk[drvreg]->sector_num.sd;
575         int position = get_cur_position();
576         
577         if(position > disk[drvreg]->sync_position[sector_num - 1]) {
578                 position -= disk[drvreg]->get_track_size();
579         }
580         
581         // first scanned sector
582         int first_sector = 0;
583         for(int i = 0; i < sector_num; i++) {
584                 if(position < disk[drvreg]->sync_position[i]) {
585                         first_sector = i;
586                         break;
587                 }
588         }
589         
590         // scan sectors
591         for(int i = 0; i < sector_num; i++) {
592                 // get sector
593                 int index = (first_sector + i) % sector_num;
594                 disk[drvreg]->get_sector(-1, -1, index);
595                 
596                 // check id
597                 if(disk[drvreg]->id[0] == 0xff) {
598                         return FDC_STA_ID_ERROR;
599                 }
600                 if(disk[drvreg]->id[0] != trkreg) {
601                         trkreg = disk[drvreg]->id[0];
602                         return FDC_STA_SEEK_ERROR;
603                 }
604                 if(disk[drvreg]->id[2] != secreg) {
605                         continue;
606                 }
607                 if(disk[drvreg]->sector_size.sd == 0) {
608                         continue;
609                 }
610                 if(disk[drvreg]->addr_crc_error && !disk[drvreg]->ignore_crc()) {
611                         // id crc error
612                         disk[drvreg]->sector_size.sd = 0;
613                         return FDC_STA_ID_MISSING; // is this okay ???
614                 }
615                 
616                 // sector found
617                 if(cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM) {
618                         fdc[drvreg].next_trans_position = disk[drvreg]->id_position[index] + 4 + 2;
619                         fdc[drvreg].bytes_before_2nd_rqm = disk[drvreg]->data_position[index] - fdc[drvreg].next_trans_position;
620                 } else {
621                         fdc[drvreg].next_trans_position = disk[drvreg]->data_position[index] + 1;
622                 }
623                 fdc[drvreg].next_sync_position = disk[drvreg]->sync_position[index];
624                 fdc[drvreg].index = 0;
625 //#ifdef _FDC_DEBUG_LOG
626                 if(_fdc_debug_log) this->out_debug_log(_T("FDC\tSECTOR FOUND SIZE=$%04x ID=%02x %02x %02x %02x CRC=%02x %02x CRC_ERROR=%d\n"),
627                         disk[drvreg]->sector_size.sd,
628                         disk[drvreg]->id[0], disk[drvreg]->id[1], disk[drvreg]->id[2], disk[drvreg]->id[3],
629                         disk[drvreg]->id[4], disk[drvreg]->id[5],
630                         disk[drvreg]->data_crc_error ? 1 : 0);
631 //#endif
632                 if(disk[drvreg]->data_crc_error && !disk[drvreg]->ignore_crc()) {
633                         return FDC_STA_DATA_ERROR;
634                 } else if(disk[drvreg]->deleted || cmdreg == FDC_CMD_WRITE_DDM) {
635                         return FDC_STA_SUCCESS_DDM;
636                 } else {
637                         return FDC_STA_SUCCESS;
638                 }
639         }
640         
641         // sector not found
642         disk[drvreg]->sector_size.sd = 0;
643         return FDC_STA_ID_MISSING;
644 }
645
646 // ----------------------------------------------------------------------------
647 // timing
648 // ----------------------------------------------------------------------------
649
650 int T3444A::get_cur_position()
651 {
652         return (fdc[drvreg].cur_position + disk[drvreg]->get_bytes_per_usec(get_passed_usec(fdc[drvreg].prev_clock))) % disk[drvreg]->get_track_size();
653 }
654
655 double T3444A::get_usec_to_start_trans()
656 {
657         // get time from current position
658         double time = get_usec_to_next_trans_pos();
659         return time;
660 }
661
662 double T3444A::get_usec_to_next_trans_pos()
663 {
664         int position = get_cur_position();
665         
666         if(disk[drvreg]->invalid_format) {
667                 // XXX: this track is invalid format and the calculated sector position may be incorrect.
668                 // so use the constant period
669                 return 50000;
670         } else if(/*disk[drvreg]->no_skew &&*/ !disk[drvreg]->correct_timing()) {
671                 // XXX: this image may be a standard image or coverted from a standard image and skew may be incorrect,
672                 // so use the period to search the next sector from the current position
673                 int sector_num = disk[drvreg]->sector_num.sd;
674                 int bytes = -1;
675                 
676                 if(position > disk[drvreg]->sync_position[sector_num - 1]) {
677                         position -= disk[drvreg]->get_track_size();
678                 }
679                 for(int i = 0; i < sector_num; i++) {
680                         if(position < disk[drvreg]->sync_position[i]) {
681                                 if(cmdreg == FDC_CMD_WRITE || cmdreg == FDC_CMD_WRITE_DDM) {
682                                         bytes = (disk[drvreg]->id_position[i] + 4 + 2) - position;
683                                 } else {
684                                         bytes = (disk[drvreg]->data_position[i] + 1) - position;
685                                 }
686                                 if(bytes < 0) {
687                                         bytes += disk[drvreg]->get_track_size(); // to make sure
688                                 }
689                                 break;
690                         }
691                 }
692                 if(bytes > 0) {
693                         return disk[drvreg]->get_usec_per_bytes(bytes);
694                 }
695                 return 50000;
696         }
697         int bytes = fdc[drvreg].next_trans_position - position;
698         if(fdc[drvreg].next_sync_position < position || bytes < 0) {
699                 bytes += disk[drvreg]->get_track_size();
700         }
701         return disk[drvreg]->get_usec_per_bytes(bytes);
702 }
703
704 double T3444A::get_usec_to_detect_index_hole(int count)
705 {
706         int position = get_cur_position();
707         int bytes = disk[drvreg]->get_track_size() * count - position;
708         if(bytes < 0) {
709                 bytes += disk[drvreg]->get_track_size();
710         }
711         return disk[drvreg]->get_usec_per_bytes(bytes);
712 }
713
714 // ----------------------------------------------------------------------------
715 // rqm
716 // ----------------------------------------------------------------------------
717
718 void T3444A::set_rqm(bool val)
719 {
720         write_signals(&outputs_rqm, (rqm = val) ? 0xffffffff : 0);
721 }
722
723 // ----------------------------------------------------------------------------
724 // user interface
725 // ----------------------------------------------------------------------------
726
727 void T3444A::open_disk(int drv, const _TCHAR* file_path, int bank)
728 {
729         if(drv < 4 && drv < _max_drive) {
730                 disk[drv]->open(file_path, bank);
731         }
732 }
733
734 void T3444A::close_disk(int drv)
735 {
736         if(drv < 4 && drv < _max_drive) {
737                 disk[drv]->close();
738                 update_head_flag(drvreg, false);
739         }
740 }
741
742 bool T3444A::is_disk_inserted(int drv)
743 {
744         if(drv < 4 && drv < _max_drive) {
745                 return disk[drv]->inserted;
746         }
747         return false;
748 }
749
750 void T3444A::is_disk_protected(int drv, bool value)
751 {
752         if(drv < 4 && drv < _max_drive) {
753                 disk[drv]->write_protected = value;
754         }
755 }
756
757 bool T3444A::is_disk_protected(int drv)
758 {
759         if(drv < 4 && drv < _max_drive) {
760                 return disk[drv]->write_protected;
761         }
762         return false;
763 }
764
765 void T3444A::set_drive_type(int drv, uint8_t type)
766 {
767         if(drv < 4 && drv < _max_drive) {
768                 disk[drv]->drive_type = type;
769         }
770 }
771
772 uint8_t T3444A::get_drive_type(int drv)
773 {
774         if(drv < 4 && drv < _max_drive) {
775                 return disk[drv]->drive_type;
776         }
777         return DRIVE_TYPE_UNK;
778 }
779
780 void T3444A::set_drive_rpm(int drv, int rpm)
781 {
782         if(drv < 4 && drv < _max_drive) {
783                 disk[drv]->drive_rpm = rpm;
784         }
785 }
786
787 void T3444A::set_drive_mfm(int drv, bool mfm)
788 {
789         if(drv < 4 && drv < _max_drive) {
790                 disk[drv]->drive_mfm = mfm;
791         }
792 }
793
794 void T3444A::update_config()
795 {
796         if(d_noise_seek != NULL) {
797                 d_noise_seek->set_mute(!config.sound_noise_fdd);
798         }
799         _fdc_debug_log = config.special_debug_fdc;
800 }
801
802 #define STATE_VERSION   2
803
804 bool T3444A::process_state(FILEIO* state_fio, bool loading)
805 {
806         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
807                 return false;
808         }
809         if(!state_fio->StateCheckInt32(this_device_id)) {
810                 return false;
811         }
812         
813         for(int i = 0; i < array_length(fdc); i++) {
814                 state_fio->StateValue(fdc[i].track);
815                 state_fio->StateValue(fdc[i].index);
816                 state_fio->StateValue(fdc[i].access);
817                 state_fio->StateValue(fdc[i].head_load);
818                 state_fio->StateValue(fdc[i].cur_position);
819                 state_fio->StateValue(fdc[i].next_trans_position);
820                 state_fio->StateValue(fdc[i].bytes_before_2nd_rqm);
821                 state_fio->StateValue(fdc[i].next_sync_position);
822                 state_fio->StateValue(fdc[i].prev_clock);
823         }
824         for(int i = 0; i < array_length(disk); i++) {
825                 if(disk[i] == NULL) continue;
826                 if(!disk[i]->process_state(state_fio, loading)) {
827                         return false;
828                 }
829         }
830         state_fio->StateValue(status);
831         state_fio->StateValue(cmdreg);
832         state_fio->StateValue(trkreg);
833         state_fio->StateValue(secreg);
834         state_fio->StateValue(datareg);
835         state_fio->StateValue(drvreg);
836         state_fio->StateValue(sidereg);
837         state_fio->StateValue(timerflag);
838         state_fio->StateArray(sector_id, sizeof(sector_id), 1);
839         state_fio->StateArray(register_id, sizeof(register_id), 1);
840         state_fio->StateValue(now_search);
841         state_fio->StateValue(seektrk);
842         state_fio->StateValue(rqm);
843         state_fio->StateValue(tnd);
844         state_fio->StateValue(motor_on);
845         state_fio->StateValue(prev_rqm_clock);
846         return true;
847 }