2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
10 #include "scsi_cdrom.h"
17 #define CDDA_PLAYING 1
19 #define _SCSI_DEBUG_LOG
20 #define _CDROM_DEBUG_LOG
21 // 0-99 is reserved for SCSI_DEV class
22 #define EVENT_CDDA 100
23 #define EVENT_CDDA_DELAY_PLAY 101
24 #define EVENT_CDROM_SEEK_SCSI 102
25 #define EVENT_CDROM_DELAY_INTERRUPT_ON 103
26 #define EVENT_CDROM_DELAY_INTERRUPT_OFF 104
27 #define EVENT_CDDA_DELAY_STOP 105
29 void SCSI_CDROM::initialize()
31 SCSI_DEV::initialize();
32 fio_img = new FILEIO();
34 if(44100 % emu->get_sound_rate() == 0) {
35 mix_loop_num = 44100 / emu->get_sound_rate();
40 event_cdda_delay_play = -1;
41 event_delay_interrupt = -1;
42 cdda_status = CDDA_OFF;
46 for(int i = 0; i < 99; i++) {
47 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
51 void SCSI_CDROM::release()
53 if(fio_img->IsOpened()) {
60 void SCSI_CDROM::reset()
63 if(event_delay_interrupt != -1) cancel_event(this, event_delay_interrupt);
64 if(event_cdda_delay_play != -1) cancel_event(this, event_cdda_delay_play);
65 if(event_cdda != -1) cancel_event(this, event_cdda);
67 event_cdda_delay_play = -1;
68 event_delay_interrupt = -1;
70 set_cdda_status(CDDA_OFF);
72 // Q: Does not seek to track 0? 20181118 K.O
77 uint32_t SCSI_CDROM::read_signal(int id)
80 case SIG_SCSI_CDROM_PLAYING:
81 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
83 case SIG_SCSI_CDROM_SAMPLE_L:
84 return (uint32_t)abs(cdda_sample_l);
86 case SIG_SCSI_CDROM_SAMPLE_R:
87 return (uint32_t)abs(cdda_sample_r);
89 return SCSI_DEV::read_signal(id);
92 void SCSI_CDROM::event_callback(int event_id, int err)
95 case EVENT_CDROM_DELAY_INTERRUPT_ON:
96 write_signals(&outputs_done, 0xffffffff);
97 event_delay_interrupt = -1;
99 case EVENT_CDROM_DELAY_INTERRUPT_OFF:
100 write_signals(&outputs_done, 0x00000000);
101 event_delay_interrupt = -1;
103 case EVENT_CDDA_DELAY_PLAY:
104 if(cdda_status != CDDA_PLAYING) {
105 set_cdda_status(CDDA_PLAYING);
107 event_cdda_delay_play = -1;
109 case EVENT_CDROM_SEEK_SCSI:
111 event_cdda_delay_play = -1;
112 SCSI_DEV::start_command();
115 if(event_cdda_delay_play > -1) return; // WAIT for SEEK COMPLETED
116 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
118 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
119 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
120 // ToDo: CLEAR IRQ Line (for PCE)
121 if((cdda_buffer_ptr += 4) % 2352 == 0) {
122 // one frame finished
123 if(++cdda_playing_frame == cdda_end_frame) {
124 // reached to end frame
125 #ifdef _CDROM_DEBUG_LOG
126 this->out_debug_log(_T("Reaches to the end of track.(FRAME %d). START_FRAME=%d END_FRAME=%d REPEAT=%s INTERRUPT=%s\n"),
127 cdda_playing_frame, cdda_start_frame, cdda_end_frame,
128 (cdda_repeat) ? _T("YES") : _T("NO"), (cdda_interrupt) ? _T("YES") : _T("NO"));
135 //int trk = get_track(cdda_start_frame);
136 int trk = current_track;
137 fio_img->Fseek((cdda_start_frame - toc_table[trk].lba_offset) * 2352, FILEIO_SEEK_SET);
139 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
141 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t) , array_length(cdda_buffer) / 2352);
143 cdda_playing_frame = cdda_start_frame;
147 //if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
148 set_cdda_status(CDDA_OFF);
149 //register_event(this, EVENT_CDDA_DELAY_STOP, 1000.0, false, &event_cdda_delay_play);
151 } else if((cdda_buffer_ptr % 2352) == 0) {
154 if(read_sectors <= 0) {
155 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
163 case EVENT_CDDA_DELAY_STOP:
165 write_signals(&outputs_done, 0xffffffff);
167 set_cdda_status(CDDA_OFF);
168 event_cdda_delay_play = -1;
171 SCSI_DEV::event_callback(event_id, err);
176 void SCSI_CDROM::set_cdda_status(uint8_t status)
178 if(status == CDDA_PLAYING) {
179 if(mix_loop_num == 0) {
180 if(event_cdda == -1) {
181 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
184 if(cdda_status != CDDA_PLAYING) {
185 //// Notify to release bus.
186 write_signals(&outputs_done, 0x00000000);
187 if(cdda_status == CDDA_OFF) {
188 //get_track_by_track_num(current_track); // Re-Play
189 //memset(cdda_buffer, 0x00, sizeof(cdda_buffer));
190 cdda_playing_frame = cdda_start_frame;
191 get_track(cdda_playing_frame);
193 } else if(cdda_status == CDDA_PAUSED) {
198 set_realtime_render(this, true);
199 #ifdef _CDROM_DEBUG_LOG
200 this->out_debug_log(_T("Play CDDA from %s.\n"), (cdda_status == CDDA_PAUSED) ? _T("PAUSED") : _T("STOPPED"));
204 if(event_cdda != -1) {
205 cancel_event(this, event_cdda);
208 if(cdda_status == CDDA_PLAYING) {
209 // Notify to release bus.
210 write_signals(&outputs_done, 0x00000000);
211 //if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
212 //register_event(this, EVENT_CDROM_DELAY_INTERRUPT_OFF, 1.0e6 / (44100.0 * 2352), false, &event_delay_interrupt);
213 if(status == CDDA_OFF) {
214 memset(cdda_buffer, 0x00, sizeof(cdda_buffer));
218 // if(fio_img->IsOpened()) fio_img->Fclose();
223 set_realtime_render(this, false);
224 #ifdef _CDROM_DEBUG_LOG
225 this->out_debug_log(_T("%s playing CDDA.\n"), (status == CDDA_PAUSED) ? _T("PAUSE") : _T("STOP"));
229 cdda_status = status;
232 void SCSI_CDROM::reset_device()
234 set_cdda_status(CDDA_OFF);
235 SCSI_DEV::reset_device();
238 bool SCSI_CDROM::is_device_ready()
243 int SCSI_CDROM::get_command_length(int value)
253 return SCSI_DEV::get_command_length(value);
256 void SCSI_CDROM::get_track_by_track_num(int track)
258 if((track <= 0) || (track >= track_num)) {
260 if(fio_img->IsOpened()) fio_img->Fclose();
262 //if(track <= 0) current_track = 0;
263 //if(track >= track_num)current_track = track_num;
268 // ToDo: Apply audio with some codecs.
269 if((current_track != track) || !(fio_img->IsOpened())){
270 if(fio_img->IsOpened()) {
273 #ifdef _CDROM_DEBUG_LOG
274 this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
277 if((track > 0) && (track < 100) && (track < track_num)) {
278 if((strlen(track_data_path[track - 1]) <= 0) ||
279 !(fio_img->Fopen(track_data_path[track - 1], FILEIO_READ_BINARY))) {
287 current_track = track;
290 // Detect only track num.
291 int SCSI_CDROM::get_track_noop(uint32_t lba)
294 for(int i = 0; i < track_num; i++) {
295 if(lba >= toc_table[i].index0) {
304 int SCSI_CDROM::get_track(uint32_t lba)
307 track = get_track_noop(lba);
309 get_track_by_track_num(track);
311 current_track = track;
316 double SCSI_CDROM::get_seek_time(uint32_t lba)
318 if(fio_img->IsOpened()) {
319 uint32_t cur_position = (uint32_t)fio_img->Ftell();
323 for(int i = 0; i < track_num; i++) {
324 if(lba >= toc_table[i].index0) {
330 distance = abs((int)(lba * physical_block_size()) - (int)(cur_position + toc_table[current_track].lba_offset * physical_block_size()));
331 if(track != current_track) {
332 current_track = get_track(lba);
335 distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
337 double ratio = ((double)distance / 333000.0) / physical_block_size(); // 333000: sectors in media
338 return max(10, (int)(400000 * 2 * ratio));
340 return 400000; // 400msec
344 uint32_t SCSI_CDROM::lba_to_msf(uint32_t lba)
346 uint8_t m = lba / (60 * 75);
347 lba -= m * (60 * 75);
348 uint8_t s = lba / 75;
349 uint8_t f = lba % 75;
351 return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
354 uint32_t SCSI_CDROM::lba_to_msf_alt(uint32_t lba)
357 ret |= ((lba / (60 * 75)) & 0xff) << 16;
358 ret |= (((lba / 75) % 60) & 0xff) << 8;
359 ret |= ((lba % 75) & 0xff) << 0;
363 void SCSI_CDROM::start_command()
366 #ifdef _SCSI_DEBUG_LOG
367 // this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: #%02x %02x %02x %02x %02x %02x\n"), scsi_id, command[0], command[1], command[2], command[3], command[4], command[5]);
371 //seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
372 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
373 set_cdda_status(CDDA_OFF);
374 if(seek_time > 10.0) {
375 if(event_cdda_delay_play >= 0) {
376 cancel_event(this, event_cdda_delay_play);
377 event_cdda_delay_play = -1;
379 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
385 case SCSI_CMD_READ10:
386 case SCSI_CMD_READ12:
387 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
388 set_cdda_status(CDDA_OFF);
389 if(seek_time > 10.0) {
390 if(event_cdda_delay_play >= 0) {
391 cancel_event(this, event_cdda_delay_play);
392 event_cdda_delay_play = -1;
394 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
402 #ifdef _SCSI_DEBUG_LOG
403 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback Start Position CMD=%02x ARG=%02x %02x %02x %02x\n"), scsi_id, command[9], command[2], command[3], command[4], command[5]);
405 if(is_device_ready()) {
406 if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
407 // stop cd-da if all params are zero
408 cdda_start_frame = 0;
409 cdda_end_frame = toc_table[track_num].index0; // end of disc
410 double seek_time = get_seek_time(cdda_end_frame);
412 get_track_by_track_num(track_num);
414 //set_cdda_status(CDDA_OFF);
415 if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
416 register_event(this, EVENT_CDDA_DELAY_STOP, (seek_time > 10.0) ? seek_time : 10.0, false, &event_cdda_delay_play);
418 uint32_t seek_offset = 0;
419 switch(command[9] & 0xc0) {
421 cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
425 uint8_t m = FROM_BCD(command[2]);
426 uint8_t s = FROM_BCD(command[3]);
427 uint8_t f = FROM_BCD(command[4]);
428 cdda_start_frame = f + 75 * (s + m * 60);
430 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
431 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
432 int track = get_track(cdda_start_frame);
433 if(cdda_start_frame >= toc_table[track].pregap) {
434 cdda_start_frame -= toc_table[track].pregap;
436 if(cdda_start_frame < toc_table[track].index0) {
437 cdda_start_frame = toc_table[track].index0; // don't play pregap
438 } else if(cdda_start_frame > max_logical_block) {
439 cdda_start_frame = 0;
445 int8_t track = FROM_BCD(command[2] - 1);
446 cdda_start_frame = toc_table[track].index1;
447 if(cdda_start_frame >= toc_table[track].pregap) {
448 cdda_start_frame -= toc_table[track].pregap;
453 cdda_start_frame = 0;
456 // if(cdda_status == CDDA_PAUSED) {
457 // cdda_end_frame = toc_table[track_num].index0; // end of disc
458 // set_cdda_status(CDDA_OFF);
460 double delay_time = get_seek_time(cdda_start_frame);
461 if((command[1] & 3) != 0) {
462 if((is_cue) && (current_track != track_num)){
463 get_track_by_track_num(track_num);
465 cdda_end_frame = toc_table[track_num].index0; // end of disc
466 double seek_time = get_seek_time(cdda_start_frame);
467 if(event_cdda_delay_play >= 0) {
468 cancel_event(this, event_cdda_delay_play);
469 event_cdda_delay_play = -1;
471 //set_cdda_status(CDDA_PLAYING);
472 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time, false, &event_cdda_delay_play);
474 uint32_t _sframe = cdda_start_frame;
475 //cdda_end_frame = toc_table[get_track(_sframe) + 1].index1; // end of this track
476 //cdda_end_frame = toc_table[get_track(_sframe)].index1; // end of this track
477 set_cdda_status(CDDA_PAUSED);
480 cdda_interrupt = ((command[1] & 3) == 2);
483 //double delay_time = 10.0;
486 //if(cdda_start_frame > 150) cdda_start_frame = cdda_start_frame - 150;
487 fio_img->Fseek((cdda_start_frame - toc_table[current_track].index0) * 2352, FILEIO_SEEK_SET);
489 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
492 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
494 cdda_playing_frame = cdda_start_frame;
497 // change to status phase
498 set_dat(SCSI_STATUS_GOOD);
500 if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
501 register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, delay_time, false, &event_delay_interrupt);
502 set_phase_delay(SCSI_PHASE_STATUS, delay_time + 10.0);
503 //write_signals(&outputs_done, 0xffffffff);
504 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
508 // change to status phase
509 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
511 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
512 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
517 #ifdef _SCSI_DEBUG_LOG
518 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback End Position CMD=%02x ARG=%02x %02x %02x %02x\n"), scsi_id, command[9], command[2], command[3], command[4], command[5]);
520 if(is_device_ready()) {
522 switch(command[9] & 0xc0) {
524 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
528 uint8_t m = FROM_BCD(command[2]);
529 uint8_t s = FROM_BCD(command[3]);
530 uint8_t f = FROM_BCD(command[4]);
531 cdda_end_frame = f + 75 * (s + m * 60);
533 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
534 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
536 // int track = get_track(cdda_start_frame);
537 int track = current_track;
538 cdda_playing_frame = cdda_start_frame;
540 // fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
542 // fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
544 //read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
546 if(cdda_end_frame > toc_table[track + 1].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track + 1].index1) {
547 cdda_end_frame = toc_table[track + 1].index1;
554 // ToDo: Over reading boundary of both tracks.
555 int _track = FROM_BCD(command[2]) - 1;
557 cdda_end_frame = toc_table[_track].index0;
559 if(current_track != _track) {
560 get_track_by_track_num(_track);
561 cdda_start_frame = toc_table[_track].index0;
562 cdda_end_frame = toc_table[_track].lba_size + toc_table[_track].lba_offset;
563 cdda_playing_frame = cdda_start_frame;
565 fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
567 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
569 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
579 if((command[1] & 3) != 0) {
580 cdda_repeat = ((command[1] & 3) == 1);
581 cdda_interrupt = ((command[1] & 3) == 2);
583 if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
584 register_event(this, EVENT_CDDA_DELAY_PLAY, 10.0, false, &event_cdda_delay_play);
585 //set_cdda_status(CDDA_PLAYING);
587 set_cdda_status(CDDA_OFF);
588 cdda_start_frame = toc_table[track_num].index0;;
589 cdda_end_frame = toc_table[track_num].index1; // end of disc
590 double seek_time = get_seek_time(cdda_start_frame);
591 //double seek_time = 10.0;
592 get_track_by_track_num(track_num); // END OF DISC
594 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
595 // if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
596 // register_event(this, EVENT_CDDA_DELAY_STOP, (seek_time > 10.0) ? seek_time : 10.0, false, &event_cdda_delay_play);
597 if(is_device_ready()) {
598 if(event_delay_interrupt >= 0) {
599 cancel_event(this, event_delay_interrupt);
600 event_delay_interrupt = -1;
602 register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, (seek_time > 10.0) ? (seek_time + 10.0) : (10.0 + 10.0), false, &event_delay_interrupt);
604 set_phase_delay(SCSI_PHASE_STATUS, (seek_time > 10.0) ? (seek_time + 10.0) : (10.0 + 10.0));
608 // change to status phase
609 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
610 if(is_device_ready()) {
611 //write_signals(&outputs_done, 0xffffffff);
612 if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
613 register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, 10.0, false, &event_delay_interrupt);
616 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
620 #ifdef _SCSI_DEBUG_LOG
621 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
623 if(is_device_ready()) {
624 if(cdda_status == CDDA_PLAYING) {
625 set_cdda_status(CDDA_PAUSED);
628 // change to status phase
629 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
630 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
631 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
635 #ifdef _SCSI_DEBUG_LOG
636 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
638 if(is_device_ready()) {
640 uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
641 uint32_t msf_abs = lba_to_msf_alt(frame);
643 double delay_time = 10.0;
644 track = current_track;
645 if((cdda_status == CDDA_OFF) && (toc_table[track].is_audio)) { // OK? (or force ERROR) 20181120 K.O
646 //set_cdda_status(CDDA_PLAYING);
647 delay_time = get_seek_time(frame);
649 if(event_cdda_delay_play >= 0) {
650 cancel_event(this, event_cdda_delay_play);
651 event_cdda_delay_play = -1;
653 register_event(this, EVENT_CDDA_DELAY_PLAY, delay_time, false, &event_cdda_delay_play);
655 uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
657 buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
658 buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
659 buffer->write(TO_BCD(track + 1)); // Track
660 buffer->write(0x01); // Index
661 buffer->write(TO_BCD((msf_rel >> 16) & 0xff)); // M (relative)
662 buffer->write(TO_BCD((msf_rel >> 8) & 0xff)); // S (relative)
663 buffer->write(TO_BCD((msf_rel >> 0) & 0xff)); // F (relative)
664 buffer->write(TO_BCD((msf_abs >> 16) & 0xff)); // M (absolute)
665 buffer->write(TO_BCD((msf_abs >> 8) & 0xff)); // S (absolute)
666 buffer->write(TO_BCD((msf_abs >> 0) & 0xff)); // F (absolute)
668 remain = buffer->count();
670 set_dat(buffer->read());
671 // change to data in phase
672 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
674 // change to status phase
675 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
676 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
681 #ifdef _SCSI_DEBUG_LOG
682 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
684 if(is_device_ready()) {
686 #ifdef _SCSI_DEBUG_LOG
687 this->out_debug_log(_T("[SCSI_DEV:ID=%d] CMD=%02x ARG=%02x \n"), scsi_id, command[1], command[2]);
690 case 0x00: /* Get first and last track numbers */
691 buffer->write(TO_BCD(1));
692 buffer->write(TO_BCD(track_num));
694 case 0x01: /* Get total disk size in MSF format */
696 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
697 buffer->write((msf >> 16) & 0xff);
698 buffer->write((msf >> 8) & 0xff);
699 buffer->write((msf >> 0) & 0xff);
702 case 0x02: /* Get track information */
703 if(command[2] == 0xaa) {
704 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
705 buffer->write((msf >> 16) & 0xff);
706 buffer->write((msf >> 8) & 0xff);
707 buffer->write((msf >> 0) & 0xff);
708 buffer->write(0x04); // correct ?
710 int track = max(FROM_BCD(command[2]), 1);
711 uint32_t frame = toc_table[track].index0;
712 // PCE wants the start sector for data tracks to *not* include the pregap
713 if(!toc_table[track].is_audio) {
714 frame += toc_table[track].pregap;
716 get_track_by_track_num(track);
718 uint32_t msf = lba_to_msf(toc_table[track].index1 + 150);
719 buffer->write((msf >> 16) & 0xff); // M
720 buffer->write((msf >> 8) & 0xff); // S
721 buffer->write((msf >> 0) & 0xff); // F
722 buffer->write(toc_table[track].is_audio ? 0x00 : 0x04);
727 remain = buffer->count();
729 set_dat(buffer->read());
730 // change to data in phase
731 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
733 // change to status phase
734 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
735 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
740 set_dat(SCSI_STATUS_CHKCOND);
744 // start standard command
745 SCSI_DEV::start_command();
748 bool SCSI_CDROM::read_buffer(int length)
750 if(!fio_img->IsOpened()) {
751 set_sense_code(SCSI_SENSE_NOTREADY);
754 uint32_t offset = (uint32_t)(position % 2352);
757 // ToDo: Need seek wait.
758 #ifdef _CDROM_DEBUG_LOG
759 this->out_debug_log(_T("Seek to LBA %d\n"), position / 2352);
761 if(fio_img->Fseek(((long)position - (long)(toc_table[current_track].lba_offset * 2352)), FILEIO_SEEK_SET) != 0) {
762 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
763 #ifdef _SCSI_DEBUG_LOG
764 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
769 if(fio_img->Fseek((long)position, FILEIO_SEEK_SET) != 0) {
770 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
771 #ifdef _SCSI_DEBUG_LOG
772 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
778 uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
779 int tmp_length = min(length, (int)sizeof(tmp_buffer));
781 if(fio_img->Fread(tmp_buffer, tmp_length, 1) != 1) {
782 #ifdef _SCSI_DEBUG_LOG
783 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
786 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
789 for(int i = 0; i < tmp_length && length > 0; i++) {
790 if(offset >= 16 && offset < 16 + 2048) {
791 int value = tmp_buffer[i];
792 buffer->write(value);
796 offset = (offset + 1) % 2352;
800 // Is This right? 20181120 K.O
801 write_signals(&outputs_done, 0xffffffff);
802 set_sense_code(SCSI_SENSE_NOSENSE);
806 int get_frames_from_msf(const char *string)
808 const char *ptr = string;
813 if(*ptr >= '0' && *ptr <= '9') {
814 frames[index] = frames[index] * 10 + (*ptr - '0');
815 } else if(*ptr == ':') {
820 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
826 return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
829 int hexatoi(const char *string)
831 const char *ptr = string;
835 if(*ptr >= '0' && *ptr <= '9') {
836 value = value * 16 + (*ptr - '0');
837 } else if(*ptr >= 'a' && *ptr <= 'f') {
838 value = value * 16 + (*ptr - 'a' + 10);
839 } else if(*ptr >= 'A' && *ptr <= 'F') {
840 value = value * 16 + (*ptr - 'A' + 10);
841 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
851 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
853 std::string line_buf;
854 std::string line_buf_shadow;
855 std::string image_tmp_data_path;
857 _TCHAR full_path_cue[_MAX_PATH];
861 int nr_current_track = 0;
862 FILEIO* fio = new FILEIO();
863 if(fio == NULL) return false;
865 memset(full_path_cue, 0x00, sizeof(full_path_cue));
866 image_tmp_data_path.clear();
868 get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
870 _TCHAR *parent_dir = get_parent_dir(full_path_cue);
882 if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
884 for(int i = 0; i < 100; i++) {
885 memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
896 if((_c == '\0') || (_c == '\n') || (_c == EOF)) break;;
897 if(_c != '\r') line_buf.push_back((char)_c);
899 if(_c == EOF) is_eof = true;
900 slen = (int)line_buf.length();
901 if(slen <= 0) goto _n_continue;
902 // Trim head of Space or TAB
910 ptr = line_buf.find_first_not_of((const char*)" \t");
911 if(ptr == std::string::npos) {
915 line_buf_shadow = line_buf.substr(ptr);
917 _arg1_ptr = line_buf_shadow.find_first_of((const char *)" \t");
918 _arg1 = line_buf_shadow.substr(0, _arg1_ptr);
919 _arg2 = line_buf_shadow.substr(_arg1_ptr);
920 std::transform(_arg1.begin(), _arg1.end(), _arg1.begin(),
921 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
923 _arg2_ptr = _arg2.find_first_not_of((const char *)" \t");
925 if(_arg2_ptr != std::string::npos) {
926 _arg2 = _arg2.substr(_arg2_ptr);
932 } else if(_arg1 == "FILE") {
933 _arg2_ptr = _arg2.find_first_of((const char *)"\"") + 1;
934 if(_arg2_ptr == std::string::npos) goto _n_continue;
936 _arg2 = _arg2.substr(_arg2_ptr);
937 _arg3_ptr = _arg2.find_first_of((const char *)"\"");
938 if(_arg3_ptr == std::string::npos) goto _n_continue;
939 _arg2 = _arg2.substr(0, _arg3_ptr);
941 image_tmp_data_path.clear();
942 image_tmp_data_path = std::string(parent_dir);
943 image_tmp_data_path.append(_arg2);
945 #ifdef _CDROM_DEBUG_LOG
946 this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path.c_str());
948 goto _n_continue; // ToDo: Check ARG2 (BINARY etc..) 20181118 K.O
949 } else if(_arg1 == "TRACK") {
950 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
952 _arg3 = _arg2.substr(_arg2_ptr_s);
953 _arg2 = _arg2.substr(0, _arg2_ptr_s);
954 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
955 int _nr_num = atoi(_arg2.c_str());
958 if((_nr_num > 0) && (_nr_num < 100) && (_arg3_ptr != std::string::npos)) {
959 nr_current_track = _nr_num;
960 _arg3 = _arg3.substr(_arg3_ptr);
962 memset(track_data_path[_nr_num - 1], 0x00, sizeof(_TCHAR) * _MAX_PATH);
963 strncpy((char *)(track_data_path[_nr_num - 1]), image_tmp_data_path.c_str(), _MAX_PATH);
965 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t\n");
966 _arg3.substr(0, _arg3_ptr_s);
968 std::transform(_arg3.begin(), _arg3.end(), _arg3.begin(),
969 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
971 toc_table[nr_current_track].is_audio = false;
972 toc_table[nr_current_track].index0 = 0;
973 toc_table[nr_current_track].index1 = 0;
974 toc_table[nr_current_track].pregap = 0;
976 if(_arg3.compare("AUDIO") == 0) {
977 toc_table[nr_current_track].is_audio = true;
978 } else if(_arg3.compare("MODE1/2352") == 0) {
979 toc_table[nr_current_track].is_audio = false;
981 // ToDo: another type
983 if(track_num < (_nr_num + 1)) track_num = _nr_num + 1;
985 // ToDo: 20181118 K.Ohta
986 nr_current_track = 0;
989 } else if(_arg1 == "INDEX") {
991 if((nr_current_track > 0) && (nr_current_track < 100)) {
992 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
993 if(_arg2_ptr_s == std::string::npos) goto _n_continue;
995 _arg3 = _arg2.substr(_arg2_ptr_s);
996 _arg2 = _arg2.substr(0, _arg2_ptr_s);
997 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
998 if(_arg3_ptr == std::string::npos) goto _n_continue;
1000 _arg3 = _arg3.substr(_arg3_ptr);
1001 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t");
1002 _arg3.substr(0, _arg3_ptr_s);
1003 int index_type = atoi(_arg2.c_str());
1005 switch(index_type) {
1007 toc_table[nr_current_track].index0 = get_frames_from_msf(_arg3.c_str());
1010 toc_table[nr_current_track].index1 = get_frames_from_msf(_arg3.c_str());
1019 } else if(_arg1 == "PREGAP") {
1020 if((nr_current_track > 0) && (nr_current_track < 100)) {
1021 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
1022 _arg2 = _arg2.substr(0, _arg2_ptr_s - 1);
1024 toc_table[nr_current_track].pregap = get_frames_from_msf(_arg2.c_str());
1036 max_logical_block = 0;
1038 toc_table[0].lba_offset = 0;
1039 toc_table[0].lba_size = 0;
1040 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1043 for(int i = 1; i < track_num; i++) {
1045 if(fio_img->IsOpened()) {
1048 if(toc_table[i].index1 != 0) {
1049 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
1050 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
1051 if(toc_table[i].index0 != max_logical_block) {
1052 if(toc_table[i].pregap == 0) {
1053 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1057 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
1058 if(toc_table[i].index0 == 0) {
1059 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1061 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
1062 if(toc_table[i].pregap == 0) {
1063 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1068 if(toc_table[i].pregap <= 0) {
1069 toc_table[i].pregap = 150; // Default PREGAP must be 2Sec. From OoTake.(Only with PCE? Not with FM-Towns?)
1073 if(strlen(track_data_path[i - 1]) > 0) {
1074 if(fio_img->Fopen(track_data_path[i - 1], FILEIO_READ_BINARY)) {
1075 if((_n = fio_img->FileLength() / 2352) > 0) {
1076 max_logical_block += _n;
1083 toc_table[i].lba_size = _n;
1084 toc_table[i].lba_offset = max_logical_block - _n;
1087 //#ifdef _CDROM_DEBUG_LOG
1088 this->out_debug_log(_T("TRACK#%02d TYPE=%s PREGAP=%d INDEX0=%d INDEX1=%d LBA_SIZE=%d LBA_OFFSET=%d PATH=%s\n"),
1089 i, (toc_table[i].is_audio) ? _T("AUDIO") : _T("MODE1/2352"),
1090 toc_table[i].pregap, toc_table[i].index0, toc_table[i].index1,
1091 toc_table[i].lba_size, toc_table[i].lba_offset, track_data_path[i - 1]);
1094 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1095 // toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1096 // toc_table[track_num].lba_offset = max_logical_block;
1097 // toc_table[track_num].lba_size = 0;
1105 if(track_num > 0) is_cue = true;
1110 void SCSI_CDROM::open(const _TCHAR* file_path)
1112 _TCHAR img_file_path[_MAX_PATH];
1116 // ToDo: Process multi track cue file.Most of CDROMs contain both audio and data and more than 2 tracks.
1118 if(check_file_extension(file_path, _T(".cue"))) {
1122 open_cue_file(file_path);
1124 // get image file name
1125 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1126 if(!FILEIO::IsFileExisting(img_file_path)) {
1127 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
1128 if(!FILEIO::IsFileExisting(img_file_path)) {
1129 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1130 if(!FILEIO::IsFileExisting(img_file_path)) {
1131 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1132 if(!FILEIO::IsFileExisting(img_file_path)) {
1133 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin.gz"), get_file_path_without_extensiton(file_path));
1138 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1139 // get image file size
1140 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1142 FILEIO* fio = new FILEIO();
1143 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1144 char line[1024], *ptr;
1146 while(fio->Fgets(line, 1024) != NULL) {
1147 if(strstr(line, "FILE") != NULL) {
1149 } else if((ptr = strstr(line, "TRACK")) != NULL) {
1151 // "TRACK 02 MODE1/2352"
1153 while(*ptr == ' ' || *ptr == 0x09) {
1156 if((track = atoi(ptr)) > 0) {
1157 if(track > track_num) {
1160 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
1162 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
1163 // "PREGAP 00:02:00"
1165 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
1167 } else if((ptr = strstr(line, "INDEX")) != NULL) {
1168 // "INDEX 01 00:00:00"
1171 while(*ptr == ' ' || *ptr == 0x09) {
1174 int num = atoi(ptr);
1175 while(*ptr >= '0' && *ptr <= '9') {
1179 toc_table[track - 1].index0 = get_frames_from_msf(ptr);
1180 } else if(num == 1) {
1181 toc_table[track - 1].index1 = get_frames_from_msf(ptr);
1186 if(track_num != 0) {
1187 for(int i = 1; i < track_num; i++) {
1188 if(toc_table[i].index0 == 0) {
1189 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1190 } else if(toc_table[i].pregap == 0) {
1191 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1194 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1195 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1205 } else if(check_file_extension(file_path, _T(".ccd"))) {
1206 // get image file name
1207 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1208 if(!FILEIO::IsFileExisting(img_file_path)) {
1209 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1210 if(!FILEIO::IsFileExisting(img_file_path)) {
1211 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1214 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1217 // get image file size
1218 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1220 FILEIO* fio = new FILEIO();
1221 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1222 char line[1024], *ptr;
1224 while(fio->Fgets(line, 1024) != NULL) {
1225 if(strstr(line, "[Session ") != NULL) {
1227 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1228 if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1229 if(track > track_num) {
1233 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
1234 if(track > 0 && track < 0xa0) {
1235 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
1237 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1238 if(track > 0 && track < 0xa0) {
1239 toc_table[track - 1].pregap = atoi(ptr + 6);
1241 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1242 if(track > 0 && track < 0xa0) {
1243 toc_table[track - 1].index1 = atoi(ptr + 5);
1247 if(track_num != 0) {
1248 toc_table[0].lba_offset = 0;
1249 toc_table[0].pregap = 0;
1250 for(int i = 1; i < track_num; i++) {
1251 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1252 toc_table[i].lba_offset = toc_table[i].pregap;
1253 toc_table[i - 1].lba_size = toc_table[i].pregap - toc_table[i - 1].pregap;
1255 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1256 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1258 toc_table[track_num].lba_size = max_logical_block - toc_table[track_num - 1].lba_offset;
1260 toc_table[track_num].lba_size = 0;
1271 #ifdef _SCSI_DEBUG_LOG
1273 for(int i = 0; i < track_num + 1; i++) {
1274 uint32_t idx0_msf = lba_to_msf(toc_table[i].index0);
1275 uint32_t idx1_msf = lba_to_msf(toc_table[i].index1);
1276 uint32_t pgap_msf = lba_to_msf(toc_table[i].pregap);
1277 this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGpap=%02x:%02x:%02x\n"), i + 1,
1278 (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
1279 (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
1280 (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
1286 void SCSI_CDROM::close()
1288 if(fio_img->IsOpened()) {
1291 memset(toc_table, 0, sizeof(toc_table));
1295 set_cdda_status(CDDA_OFF);
1298 bool SCSI_CDROM::mounted()
1300 if(is_cue) return true;
1301 return fio_img->IsOpened();
1304 bool SCSI_CDROM::accessed()
1306 bool value = access;
1311 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1313 if(cdda_status == CDDA_PLAYING) {
1314 if(mix_loop_num != 0) {
1315 int tmp_l = 0, tmp_r = 0;
1316 for(int i = 0; i < mix_loop_num; i++) {
1317 event_callback(EVENT_CDDA, 0);
1318 tmp_l += cdda_sample_l;
1319 tmp_r += cdda_sample_r;
1321 cdda_sample_l = tmp_l / mix_loop_num;
1322 cdda_sample_r = tmp_r / mix_loop_num;
1324 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
1325 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
1327 for(int i = 0; i < cnt; i++) {
1328 *buffer++ += val_l; // L
1329 *buffer++ += val_r; // R
1334 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1336 volume_l = decibel_to_volume(decibel_l);
1337 volume_r = decibel_to_volume(decibel_r);
1340 void SCSI_CDROM::set_volume(int volume)
1342 volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1345 #define STATE_VERSION 4
1347 // Q: If loading state when using another (saved) image? 20181013 K.O
1348 // May need close() and open() (or ...?).
1349 bool SCSI_CDROM::process_state(FILEIO* state_fio, bool loading)
1351 uint32_t offset = 0;
1353 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1356 if(!state_fio->StateCheckInt32(this_device_id)) {
1359 state_fio->StateValue(cdda_start_frame);
1360 state_fio->StateValue(cdda_end_frame);
1361 state_fio->StateValue(cdda_playing_frame);
1362 state_fio->StateValue(cdda_status);
1363 state_fio->StateValue(cdda_repeat);
1364 state_fio->StateValue(cdda_interrupt);
1365 state_fio->StateArray(cdda_buffer, sizeof(cdda_buffer), 1);
1366 state_fio->StateValue(cdda_buffer_ptr);
1367 state_fio->StateValue(cdda_sample_l);
1368 state_fio->StateValue(cdda_sample_r);
1369 state_fio->StateValue(event_cdda);
1370 state_fio->StateValue(event_cdda_delay_play);
1371 state_fio->StateValue(event_delay_interrupt);
1372 state_fio->StateValue(read_sectors);
1373 // state_fio->StateValue(mix_loop_num);
1374 state_fio->StateValue(volume_m);
1376 offset = state_fio->FgetUint32_LE();
1378 if(fio_img->IsOpened()) {
1379 offset = fio_img->Ftell();
1381 state_fio->FputUint32_LE(offset);
1384 state_fio->StateValue(is_cue);
1385 state_fio->StateValue(current_track);
1386 // ToDo: Re-Open Image.20181118 K.O
1388 if(loading && fio_img->IsOpened()) {
1389 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1391 return SCSI_DEV::process_state(state_fio, loading);