2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
10 #include "scsi_cdrom.h"
17 #define CDDA_PLAYING 1
20 #define _SCSI_DEBUG_LOG
21 #define _CDROM_DEBUG_LOG
23 // 0-99 is reserved for SCSI_DEV class
24 #define EVENT_CDDA 100
25 #define EVENT_CDDA_DELAY_PLAY 101
26 #define EVENT_CDROM_SEEK_SCSI 102
27 #define EVENT_CDROM_DELAY_INTERRUPT_ON 103
28 #define EVENT_CDROM_DELAY_INTERRUPT_OFF 104
29 #define EVENT_CDDA_DELAY_STOP 105
31 void SCSI_CDROM::initialize()
33 SCSI_DEV::initialize();
34 fio_img = new FILEIO();
36 if(44100 % emu->get_sound_rate() == 0) {
37 mix_loop_num = 44100 / emu->get_sound_rate();
42 event_cdda_delay_play = -1;
43 event_delay_interrupt = -1;
44 cdda_status = CDDA_OFF;
48 for(int i = 0; i < 99; i++) {
49 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
53 void SCSI_CDROM::release()
55 if(fio_img->IsOpened()) {
62 void SCSI_CDROM::reset()
65 if(event_delay_interrupt != -1) cancel_event(this, event_delay_interrupt);
66 if(event_cdda_delay_play != -1) cancel_event(this, event_cdda_delay_play);
67 if(event_cdda != -1) cancel_event(this, event_cdda);
69 event_cdda_delay_play = -1;
70 event_delay_interrupt = -1;
73 set_cdda_status(CDDA_OFF);
75 // Q: Does not seek to track 0? 20181118 K.O
80 void SCSI_CDROM::write_signal(int id, uint32_t data, uint32_t mask)
82 bool _b = ((data & mask) != 0);
84 case SIG_SCSI_CDROM_CDDA_STOP:
85 if(cdda_status != CDDA_OFF) {
86 if(_b) set_cdda_status(CDDA_OFF);
89 case SIG_SCSI_CDROM_CDDA_PLAY:
90 if(cdda_status != CDDA_PLAYING) {
91 if(_b) set_cdda_status(CDDA_PLAYING);
94 case SIG_SCSI_CDROM_CDDA_PAUSE:
95 if(cdda_status != CDDA_PAUSED) {
96 if(_b) set_cdda_status(CDDA_PAUSED);
100 SCSI_DEV::write_signal(id, data, mask);
105 uint32_t SCSI_CDROM::read_signal(int id)
108 case SIG_SCSI_CDROM_PLAYING:
109 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
111 case SIG_SCSI_CDROM_SAMPLE_L:
112 return (uint32_t)abs(cdda_sample_l);
114 case SIG_SCSI_CDROM_SAMPLE_R:
115 return (uint32_t)abs(cdda_sample_r);
117 return SCSI_DEV::read_signal(id);
120 void SCSI_CDROM::event_callback(int event_id, int err)
123 case EVENT_CDROM_DELAY_INTERRUPT_ON:
124 write_signals(&outputs_done, 0xffffffff);
125 event_delay_interrupt = -1;
127 case EVENT_CDROM_DELAY_INTERRUPT_OFF:
128 write_signals(&outputs_done, 0x00000000);
129 event_delay_interrupt = -1;
131 case EVENT_CDDA_DELAY_PLAY:
132 if(cdda_status != CDDA_PLAYING) {
133 set_cdda_status(CDDA_PLAYING);
135 event_cdda_delay_play = -1;
137 case EVENT_CDROM_SEEK_SCSI:
139 event_cdda_delay_play = -1;
140 SCSI_DEV::start_command();
143 if(event_cdda_delay_play > -1) return; // WAIT for SEEK COMPLETED
144 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
146 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
147 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
148 // ToDo: CLEAR IRQ Line (for PCE)
149 if((cdda_buffer_ptr += 4) % 2352 == 0) {
150 // one frame finished
151 if(++cdda_playing_frame == cdda_end_frame) {
152 // reached to end frame
153 #ifdef _CDROM_DEBUG_LOG
154 this->out_debug_log(_T("Reaches to the end of track.(FRAME %d). START_FRAME=%d END_FRAME=%d REPEAT=%s INTERRUPT=%s\n"),
155 cdda_playing_frame, cdda_start_frame, cdda_end_frame,
156 (cdda_repeat) ? _T("YES") : _T("NO"), (cdda_interrupt) ? _T("YES") : _T("NO"));
163 //int trk = get_track(cdda_start_frame);
164 int trk = current_track;
165 fio_img->Fseek((cdda_start_frame - toc_table[trk].lba_offset) * 2352, FILEIO_SEEK_SET);
167 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
169 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t) , array_length(cdda_buffer) / 2352);
171 cdda_playing_frame = cdda_start_frame;
175 //if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
176 set_cdda_status(CDDA_OFF);
177 //register_event(this, EVENT_CDDA_DELAY_STOP, 1000.0, false, &event_cdda_delay_play);
179 } else if((cdda_buffer_ptr % 2352) == 0) {
182 if(read_sectors <= 0) {
183 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
191 case EVENT_CDDA_DELAY_STOP:
193 write_signals(&outputs_done, 0xffffffff);
195 set_cdda_status(CDDA_OFF);
196 event_cdda_delay_play = -1;
199 SCSI_DEV::event_callback(event_id, err);
204 void SCSI_CDROM::set_cdda_status(uint8_t status)
206 if(status == CDDA_PLAYING) {
207 if(mix_loop_num == 0) {
208 if(event_cdda == -1) {
209 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
212 if(cdda_status != CDDA_PLAYING) {
213 //// Notify to release bus.
214 write_signals(&outputs_done, 0x00000000);
215 if(cdda_status == CDDA_OFF) {
216 //get_track_by_track_num(current_track); // Re-Play
217 //memset(cdda_buffer, 0x00, sizeof(cdda_buffer));
218 cdda_playing_frame = cdda_start_frame;
219 get_track(cdda_playing_frame);
221 } else if(cdda_status == CDDA_PAUSED) {
226 set_realtime_render(this, true);
227 #ifdef _CDROM_DEBUG_LOG
228 this->out_debug_log(_T("Play CDDA from %s.\n"), (cdda_status == CDDA_PAUSED) ? _T("PAUSED") : _T("STOPPED"));
232 if(event_cdda != -1) {
233 cancel_event(this, event_cdda);
236 if(cdda_status == CDDA_PLAYING) {
237 // Notify to release bus.
238 write_signals(&outputs_done, 0x00000000);
239 //if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
240 //register_event(this, EVENT_CDROM_DELAY_INTERRUPT_OFF, 1.0e6 / (44100.0 * 2352), false, &event_delay_interrupt);
241 if(status == CDDA_OFF) {
242 memset(cdda_buffer, 0x00, sizeof(cdda_buffer));
246 // if(fio_img->IsOpened()) fio_img->Fclose();
251 set_realtime_render(this, false);
252 #ifdef _CDROM_DEBUG_LOG
253 this->out_debug_log(_T("%s playing CDDA.\n"), (status == CDDA_PAUSED) ? _T("PAUSE") : _T("STOP"));
257 cdda_status = status;
260 void SCSI_CDROM::reset_device()
262 set_cdda_status(CDDA_OFF);
263 SCSI_DEV::reset_device();
266 bool SCSI_CDROM::is_device_ready()
271 int SCSI_CDROM::get_command_length(int value)
281 return SCSI_DEV::get_command_length(value);
284 void SCSI_CDROM::get_track_by_track_num(int track)
286 if((track <= 0) || (track >= track_num)) {
288 if(fio_img->IsOpened()) fio_img->Fclose();
290 //if(track <= 0) current_track = 0;
291 //if(track >= track_num)current_track = track_num;
296 // ToDo: Apply audio with some codecs.
297 if((current_track != track) || !(fio_img->IsOpened())){
298 if(fio_img->IsOpened()) {
301 #ifdef _CDROM_DEBUG_LOG
302 this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
305 if((track > 0) && (track < 100) && (track < track_num)) {
306 if((strlen(track_data_path[track - 1]) <= 0) ||
307 !(fio_img->Fopen(track_data_path[track - 1], FILEIO_READ_BINARY))) {
315 current_track = track;
318 // Detect only track num.
319 int SCSI_CDROM::get_track_noop(uint32_t lba)
322 for(int i = 0; i < track_num; i++) {
323 if(lba >= toc_table[i].index0) {
332 int SCSI_CDROM::get_track(uint32_t lba)
335 track = get_track_noop(lba);
337 get_track_by_track_num(track);
339 current_track = track;
344 double SCSI_CDROM::get_seek_time(uint32_t lba)
346 if(fio_img->IsOpened()) {
347 uint32_t cur_position = (uint32_t)fio_img->Ftell();
351 for(int i = 0; i < track_num; i++) {
352 if(lba >= toc_table[i].index0) {
358 distance = abs((int)(lba * physical_block_size()) - (int)(cur_position + toc_table[current_track].lba_offset * physical_block_size()));
359 if(track != current_track) {
360 current_track = get_track(lba);
363 distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
365 double ratio = ((double)distance / 333000.0) / physical_block_size(); // 333000: sectors in media
366 return max(10, (int)(400000 * 2 * ratio));
367 //double ratio = (double)distance / 150.0e3; // 150KB/sec sectors in media
368 //return max(10, (int)(400000 * 2 * ratio));
370 return 400000; // 400msec
374 uint32_t SCSI_CDROM::lba_to_msf(uint32_t lba)
376 uint8_t m = lba / (60 * 75);
377 lba -= m * (60 * 75);
378 uint8_t s = lba / 75;
379 uint8_t f = lba % 75;
381 return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
384 uint32_t SCSI_CDROM::lba_to_msf_alt(uint32_t lba)
387 ret |= ((lba / (60 * 75)) & 0xff) << 16;
388 ret |= (((lba / 75) % 60) & 0xff) << 8;
389 ret |= ((lba % 75) & 0xff) << 0;
393 void SCSI_CDROM::start_command()
396 #ifdef _SCSI_DEBUG_LOG
397 //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]);
401 //seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
402 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
403 set_cdda_status(CDDA_OFF);
404 if(seek_time > 10.0) {
405 if(event_cdda_delay_play >= 0) {
406 cancel_event(this, event_cdda_delay_play);
407 event_cdda_delay_play = -1;
409 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time, false, &event_cdda_delay_play);
415 case SCSI_CMD_READ10:
416 case SCSI_CMD_READ12:
417 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
418 set_cdda_status(CDDA_OFF);
419 if(seek_time > 10.0) {
420 if(event_cdda_delay_play >= 0) {
421 cancel_event(this, event_cdda_delay_play);
422 event_cdda_delay_play = -1;
424 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
431 case SCSI_CMD_MODE_SEL6:
432 #ifdef _SCSI_DEBUG_LOG
433 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Mode Select 6-byte\n"), scsi_id);
436 // position = (command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3];
437 // position *= physical_block_size();
440 // remain = command[4];// * logical_block_size();
445 // change to data in phase
446 set_phase_delay(SCSI_PHASE_DATA_OUT, seek_time);
448 // transfer length is zero, change to status phase
449 set_dat(SCSI_STATUS_GOOD);
450 set_sense_code(SCSI_SENSE_NOSENSE);
451 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
456 #ifdef _SCSI_DEBUG_LOG
457 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]);
459 if(is_device_ready()) {
460 if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
461 // stop cd-da if all params are zero
462 cdda_start_frame = 0;
463 cdda_end_frame = toc_table[track_num].index0; // end of disc
464 double seek_time = get_seek_time(cdda_end_frame);
466 get_track_by_track_num(track_num);
468 //set_cdda_status(CDDA_OFF);
469 if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
470 register_event(this, EVENT_CDDA_DELAY_STOP, (seek_time > 10.0) ? seek_time : 10.0, false, &event_cdda_delay_play);
472 uint32_t seek_offset = 0;
473 switch(command[9] & 0xc0) {
475 cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
479 uint8_t m = FROM_BCD(command[2]);
480 uint8_t s = FROM_BCD(command[3]);
481 uint8_t f = FROM_BCD(command[4]);
482 cdda_start_frame = f + 75 * (s + m * 60);
484 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
485 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
486 int track = get_track(cdda_start_frame);
487 if(cdda_start_frame >= toc_table[track].pregap) {
488 cdda_start_frame -= toc_table[track].pregap;
490 if(cdda_start_frame < toc_table[track].index0) {
491 cdda_start_frame = toc_table[track].index0; // don't play pregap
492 } else if(cdda_start_frame > max_logical_block) {
493 cdda_start_frame = 0;
499 int8_t track = FROM_BCD(command[2] - 1);
500 cdda_start_frame = toc_table[track].index1;
501 if(cdda_start_frame >= toc_table[track].pregap) {
502 cdda_start_frame -= toc_table[track].pregap;
507 cdda_start_frame = 0;
510 // if(cdda_status == CDDA_PAUSED) {
511 // cdda_end_frame = toc_table[track_num].index0; // end of disc
512 // set_cdda_status(CDDA_OFF);
514 double delay_time = get_seek_time(cdda_start_frame);
515 if((command[1] & 3) != 0) {
516 if((is_cue) && (current_track != track_num)){
517 get_track_by_track_num(track_num);
519 cdda_end_frame = toc_table[track_num].index0; // end of disc
520 double seek_time = get_seek_time(cdda_start_frame);
521 if(event_cdda_delay_play >= 0) {
522 cancel_event(this, event_cdda_delay_play);
523 event_cdda_delay_play = -1;
525 //set_cdda_status(CDDA_PLAYING);
526 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time, false, &event_cdda_delay_play);
528 uint32_t _sframe = cdda_start_frame;
529 //cdda_end_frame = toc_table[get_track(_sframe) + 1].index1; // end of this track
530 //cdda_end_frame = toc_table[get_track(_sframe)].index1; // end of this track
531 set_cdda_status(CDDA_PAUSED);
534 cdda_interrupt = ((command[1] & 3) == 2);
537 //double delay_time = 10.0;
540 //if(cdda_start_frame > 150) cdda_start_frame = cdda_start_frame - 150;
541 //fio_img->Fseek((cdda_start_frame - toc_table[current_track].index0) * 2352, FILEIO_SEEK_SET);
542 fio_img->Fseek((cdda_start_frame - toc_table[current_track].index0) * 2352, FILEIO_SEEK_SET);
544 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
547 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
549 cdda_playing_frame = cdda_start_frame;
552 // change to status phase
553 set_dat(SCSI_STATUS_GOOD);
555 if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
556 register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, delay_time, false, &event_delay_interrupt);
557 //set_phase_delay(SCSI_PHASE_STATUS, delay_time + 10.0);
558 //write_signals(&outputs_done, 0xffffffff);
559 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
563 // change to status phase
564 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
566 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
567 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
572 #ifdef _SCSI_DEBUG_LOG
573 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]);
575 if(is_device_ready()) {
577 switch(command[9] & 0xc0) {
579 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
583 uint8_t m = FROM_BCD(command[2]);
584 uint8_t s = FROM_BCD(command[3]);
585 uint8_t f = FROM_BCD(command[4]);
586 cdda_end_frame = f + 75 * (s + m * 60);
588 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
589 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
591 // int track = get_track(cdda_start_frame);
592 int track = current_track;
593 cdda_playing_frame = cdda_start_frame;
595 // fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
597 // fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
599 //read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
601 if(cdda_end_frame > toc_table[track + 1].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track + 1].index1) {
602 cdda_end_frame = toc_table[track + 1].index1;
609 int _track = FROM_BCD(command[2]) - 1;
611 cdda_end_frame = toc_table[_track].index0;
613 if(current_track != _track) {
614 get_track_by_track_num(_track);
615 cdda_start_frame = toc_table[_track].index0;
616 cdda_end_frame = toc_table[_track].lba_size + toc_table[_track].lba_offset;
617 cdda_playing_frame = cdda_start_frame;
619 fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
621 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
623 read_sectors = fio_img->Fread(cdda_buffer, 2352 * sizeof(uint8_t), array_length(cdda_buffer) / 2352);
633 if((command[1] & 3) != 0) {
634 cdda_repeat = ((command[1] & 3) == 1);
635 cdda_interrupt = ((command[1] & 3) == 2);
637 if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
638 register_event(this, EVENT_CDDA_DELAY_PLAY, 10.0, false, &event_cdda_delay_play);
639 //set_cdda_status(CDDA_PLAYING);
641 set_cdda_status(CDDA_OFF);
642 cdda_start_frame = toc_table[track_num].index0;;
643 cdda_end_frame = toc_table[track_num].index1; // end of disc
644 double seek_time = get_seek_time(cdda_start_frame);
645 //double seek_time = 10.0;
646 get_track_by_track_num(track_num); // END OF DISC
648 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
649 // if(event_cdda_delay_play >= 0) cancel_event(this, event_cdda_delay_play);
650 // register_event(this, EVENT_CDDA_DELAY_STOP, (seek_time > 10.0) ? seek_time : 10.0, false, &event_cdda_delay_play);
651 if(is_device_ready()) {
652 if(event_delay_interrupt >= 0) {
653 cancel_event(this, event_delay_interrupt);
654 event_delay_interrupt = -1;
656 register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, (seek_time > 10.0) ? (seek_time + 10.0) : (10.0 + 10.0), false, &event_delay_interrupt);
658 set_phase_delay(SCSI_PHASE_STATUS, (seek_time > 10.0) ? (seek_time + 10.0) : (10.0 + 10.0));
662 // change to status phase
663 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
664 if(is_device_ready()) {
665 write_signals(&outputs_done, 0xffffffff);
666 //if(event_delay_interrupt >= 0) cancel_event(this, event_delay_interrupt);
667 //register_event(this, EVENT_CDROM_DELAY_INTERRUPT_ON, 10.0, false, &event_delay_interrupt);
670 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
674 #ifdef _SCSI_DEBUG_LOG
675 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
677 if(is_device_ready()) {
678 if(cdda_status == CDDA_PLAYING) {
679 set_cdda_status(CDDA_PAUSED);
682 // change to status phase
683 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
684 if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
685 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
689 #ifdef _SCSI_DEBUG_LOG
690 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
692 if(is_device_ready()) {
694 uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
695 uint32_t msf_abs = lba_to_msf_alt(frame);
697 double delay_time = 10.0;
698 track = current_track;
699 if((cdda_status == CDDA_OFF) && (toc_table[track].is_audio)) { // OK? (or force ERROR) 20181120 K.O
700 //set_cdda_status(CDDA_PLAYING);
701 delay_time = get_seek_time(frame);
703 if(event_cdda_delay_play >= 0) {
704 cancel_event(this, event_cdda_delay_play);
705 event_cdda_delay_play = -1;
707 register_event(this, EVENT_CDDA_DELAY_PLAY, delay_time, false, &event_cdda_delay_play);
709 uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
711 buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
712 buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
713 buffer->write(TO_BCD(track + 1)); // Track
714 buffer->write(0x01); // Index
715 buffer->write(TO_BCD((msf_rel >> 16) & 0xff)); // M (relative)
716 buffer->write(TO_BCD((msf_rel >> 8) & 0xff)); // S (relative)
717 buffer->write(TO_BCD((msf_rel >> 0) & 0xff)); // F (relative)
718 buffer->write(TO_BCD((msf_abs >> 16) & 0xff)); // M (absolute)
719 buffer->write(TO_BCD((msf_abs >> 8) & 0xff)); // S (absolute)
720 buffer->write(TO_BCD((msf_abs >> 0) & 0xff)); // F (absolute)
722 remain = buffer->count();
724 set_dat(buffer->read());
725 // change to data in phase
726 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
728 // change to status phase
729 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
730 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
735 #ifdef _SCSI_DEBUG_LOG
736 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
738 if(is_device_ready()) {
740 #ifdef _SCSI_DEBUG_LOG
741 this->out_debug_log(_T("[SCSI_DEV:ID=%d] CMD=%02x ARG=%02x \n"), scsi_id, command[1], command[2]);
744 case 0x00: /* Get first and last track numbers */
745 buffer->write(TO_BCD(1));
746 buffer->write(TO_BCD(track_num));
747 // PC-8801 CD BIOS invites 4 bytes ?
751 case 0x01: /* Get total disk size in MSF format */
753 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
754 buffer->write((msf >> 16) & 0xff);
755 buffer->write((msf >> 8) & 0xff);
756 buffer->write((msf >> 0) & 0xff);
757 // PC-8801 CD BIOS invites 4 bytes ?
761 case 0x02: /* Get track information */
762 if(command[2] == 0xaa) {
763 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
764 buffer->write((msf >> 16) & 0xff);
765 buffer->write((msf >> 8) & 0xff);
766 buffer->write((msf >> 0) & 0xff);
767 buffer->write(0x04); // correct ?
769 int track = max(FROM_BCD(command[2]), 1);
770 uint32_t frame = toc_table[track].index0;
771 // PCE wants the start sector for data tracks to *not* include the pregap
772 if(!toc_table[track].is_audio) {
773 frame += toc_table[track].pregap;
775 get_track_by_track_num(track);
777 uint32_t msf = lba_to_msf(toc_table[track].index1 + 150);
778 buffer->write((msf >> 16) & 0xff); // M
779 buffer->write((msf >> 8) & 0xff); // S
780 buffer->write((msf >> 0) & 0xff); // F
781 buffer->write(toc_table[track].is_audio ? 0x00 : 0x04);
786 remain = buffer->count();
788 set_dat(buffer->read());
789 // change to data in phase
790 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
792 // change to status phase
793 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
794 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
799 set_dat(SCSI_STATUS_CHKCOND);
803 // start standard command
804 SCSI_DEV::start_command();
807 bool SCSI_CDROM::read_buffer(int length)
809 if(!fio_img->IsOpened()) {
810 set_sense_code(SCSI_SENSE_NOTREADY);
813 uint32_t offset = (uint32_t)(position % 2352);
816 // ToDo: Need seek wait.
817 #ifdef _CDROM_DEBUG_LOG
818 this->out_debug_log(_T("Seek to LBA %d LENGTH=%d\n"), position / 2352, length);
820 if(fio_img->Fseek(((long)position - (long)(toc_table[current_track].lba_offset * 2352)), FILEIO_SEEK_SET) != 0) {
821 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
822 #ifdef _SCSI_DEBUG_LOG
823 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
828 if(fio_img->Fseek((long)position, FILEIO_SEEK_SET) != 0) {
829 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
830 #ifdef _SCSI_DEBUG_LOG
831 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
837 uint8_t tmp_buffer[2352];
838 int tmp_length = min(length, (int)sizeof(tmp_buffer));
839 // int tmp_length = 2352 - offset;
841 if(fio_img->Fread(tmp_buffer, tmp_length, 1) != 1) {
842 #ifdef _SCSI_DEBUG_LOG
843 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
846 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
849 for(int i = 0; i < tmp_length; i++) {
850 if(offset >= 16 && offset < 16 + logical_block_size()) {
851 int value = tmp_buffer[i];
852 buffer->write(value);
856 offset = (offset + 1) % 2352;
860 // Is This right? 20181120 K.O
861 //write_signals(&outputs_done, 0xffffffff); // Maybe don't need "DONE SIGNAL" with reading DATA TRACK. 20181207 K.O
862 set_sense_code(SCSI_SENSE_NOSENSE);
866 bool SCSI_CDROM::write_buffer(int length)
868 for(int i = 0; i < length; i++) {
869 int value = buffer->read();
870 if(command[0] == SCSI_CMD_MODE_SEL6) {
872 #ifdef _SCSI_DEBUG_LOG
873 this->out_debug_log(_T("[SCSI_DEV:ID=%d] NEC Read Mode = %02X\n"), scsi_id, value);
875 read_mode = (value != 0);
877 #ifdef _SCSI_DEBUG_LOG
878 this->out_debug_log(_T("[SCSI_DEV:ID=%d] NEC Retry Count = %02X\n"), scsi_id, value);
884 set_sense_code(SCSI_SENSE_NOSENSE);
888 int get_frames_from_msf(const char *string)
890 const char *ptr = string;
895 if(*ptr >= '0' && *ptr <= '9') {
896 frames[index] = frames[index] * 10 + (*ptr - '0');
897 } else if(*ptr == ':') {
902 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
908 return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
911 int hexatoi(const char *string)
913 const char *ptr = string;
917 if(*ptr >= '0' && *ptr <= '9') {
918 value = value * 16 + (*ptr - '0');
919 } else if(*ptr >= 'a' && *ptr <= 'f') {
920 value = value * 16 + (*ptr - 'a' + 10);
921 } else if(*ptr >= 'A' && *ptr <= 'F') {
922 value = value * 16 + (*ptr - 'A' + 10);
923 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
933 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
935 std::string line_buf;
936 std::string line_buf_shadow;
937 std::string image_tmp_data_path;
939 _TCHAR full_path_cue[_MAX_PATH];
943 int nr_current_track = 0;
944 FILEIO* fio = new FILEIO();
945 if(fio == NULL) return false;
947 memset(full_path_cue, 0x00, sizeof(full_path_cue));
948 image_tmp_data_path.clear();
950 get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
952 _TCHAR *parent_dir = get_parent_dir(full_path_cue);
964 if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
966 for(int i = 0; i < 100; i++) {
967 memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
978 if((_c == '\0') || (_c == '\n') || (_c == EOF)) break;;
979 if(_c != '\r') line_buf.push_back((char)_c);
981 if(_c == EOF) is_eof = true;
982 slen = (int)line_buf.length();
983 if(slen <= 0) goto _n_continue;
984 // Trim head of Space or TAB
992 ptr = line_buf.find_first_not_of((const char*)" \t");
993 if(ptr == std::string::npos) {
997 line_buf_shadow = line_buf.substr(ptr);
999 _arg1_ptr = line_buf_shadow.find_first_of((const char *)" \t");
1000 _arg1 = line_buf_shadow.substr(0, _arg1_ptr);
1001 _arg2 = line_buf_shadow.substr(_arg1_ptr);
1002 std::transform(_arg1.begin(), _arg1.end(), _arg1.begin(),
1003 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
1005 _arg2_ptr = _arg2.find_first_not_of((const char *)" \t");
1007 if(_arg2_ptr != std::string::npos) {
1008 _arg2 = _arg2.substr(_arg2_ptr);
1011 if(_arg1 == "REM") {
1014 } else if(_arg1 == "FILE") {
1015 _arg2_ptr = _arg2.find_first_of((const char *)"\"") + 1;
1016 if(_arg2_ptr == std::string::npos) goto _n_continue;
1018 _arg2 = _arg2.substr(_arg2_ptr);
1019 _arg3_ptr = _arg2.find_first_of((const char *)"\"");
1020 if(_arg3_ptr == std::string::npos) goto _n_continue;
1021 _arg2 = _arg2.substr(0, _arg3_ptr);
1023 image_tmp_data_path.clear();
1024 image_tmp_data_path = std::string(parent_dir);
1025 image_tmp_data_path.append(_arg2);
1027 #ifdef _CDROM_DEBUG_LOG
1028 this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path.c_str());
1030 goto _n_continue; // ToDo: Check ARG2 (BINARY etc..) 20181118 K.O
1031 } else if(_arg1 == "TRACK") {
1032 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
1034 _arg3 = _arg2.substr(_arg2_ptr_s);
1035 _arg2 = _arg2.substr(0, _arg2_ptr_s);
1036 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
1037 int _nr_num = atoi(_arg2.c_str());
1040 if((_nr_num > 0) && (_nr_num < 100) && (_arg3_ptr != std::string::npos)) {
1041 nr_current_track = _nr_num;
1042 _arg3 = _arg3.substr(_arg3_ptr);
1044 memset(track_data_path[_nr_num - 1], 0x00, sizeof(_TCHAR) * _MAX_PATH);
1045 strncpy((char *)(track_data_path[_nr_num - 1]), image_tmp_data_path.c_str(), _MAX_PATH);
1047 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t\n");
1048 _arg3.substr(0, _arg3_ptr_s);
1050 std::transform(_arg3.begin(), _arg3.end(), _arg3.begin(),
1051 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
1053 toc_table[nr_current_track].is_audio = false;
1054 toc_table[nr_current_track].index0 = 0;
1055 toc_table[nr_current_track].index1 = 0;
1056 toc_table[nr_current_track].pregap = 0;
1058 if(_arg3.compare("AUDIO") == 0) {
1059 toc_table[nr_current_track].is_audio = true;
1060 } else if(_arg3.compare("MODE1/2352") == 0) {
1061 toc_table[nr_current_track].is_audio = false;
1063 // ToDo: another type
1065 if(track_num < (_nr_num + 1)) track_num = _nr_num + 1;
1067 // ToDo: 20181118 K.Ohta
1068 nr_current_track = 0;
1071 } else if(_arg1 == "INDEX") {
1073 if((nr_current_track > 0) && (nr_current_track < 100)) {
1074 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
1075 if(_arg2_ptr_s == std::string::npos) goto _n_continue;
1077 _arg3 = _arg2.substr(_arg2_ptr_s);
1078 _arg2 = _arg2.substr(0, _arg2_ptr_s);
1079 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
1080 if(_arg3_ptr == std::string::npos) goto _n_continue;
1082 _arg3 = _arg3.substr(_arg3_ptr);
1083 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t");
1084 _arg3.substr(0, _arg3_ptr_s);
1085 int index_type = atoi(_arg2.c_str());
1087 switch(index_type) {
1089 toc_table[nr_current_track].index0 = get_frames_from_msf(_arg3.c_str());
1092 toc_table[nr_current_track].index1 = get_frames_from_msf(_arg3.c_str());
1101 } else if(_arg1 == "PREGAP") {
1102 if((nr_current_track > 0) && (nr_current_track < 100)) {
1103 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
1104 _arg2 = _arg2.substr(0, _arg2_ptr_s - 1);
1106 toc_table[nr_current_track].pregap = get_frames_from_msf(_arg2.c_str());
1118 max_logical_block = 0;
1120 toc_table[0].lba_offset = 0;
1121 toc_table[0].lba_size = 0;
1122 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1125 for(int i = 1; i < track_num; i++) {
1127 if(fio_img->IsOpened()) {
1130 if(toc_table[i].index1 != 0) {
1131 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
1132 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
1133 //if(toc_table[i].index0 != max_logical_block) {
1134 if(toc_table[i].pregap == 0) {
1135 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1139 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
1140 if(toc_table[i].index0 == 0) {
1141 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1144 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
1145 if(toc_table[i].pregap == 0) {
1146 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1151 if(toc_table[i].pregap <= 0) {
1152 toc_table[i].pregap = 150; // Default PREGAP must be 2Sec. From OoTake.(Only with PCE? Not with FM-Towns?)
1156 if(strlen(track_data_path[i - 1]) > 0) {
1157 if(fio_img->Fopen(track_data_path[i - 1], FILEIO_READ_BINARY)) {
1158 if((_n = fio_img->FileLength() / 2352) > 0) {
1159 max_logical_block += _n;
1166 toc_table[i].lba_size = _n;
1167 toc_table[i].lba_offset = max_logical_block - _n;
1170 //#ifdef _CDROM_DEBUG_LOG
1171 this->out_debug_log(_T("TRACK#%02d TYPE=%s PREGAP=%d INDEX0=%d INDEX1=%d LBA_SIZE=%d LBA_OFFSET=%d PATH=%s\n"),
1172 i, (toc_table[i].is_audio) ? _T("AUDIO") : _T("MODE1/2352"),
1173 toc_table[i].pregap, toc_table[i].index0, toc_table[i].index1,
1174 toc_table[i].lba_size, toc_table[i].lba_offset, track_data_path[i - 1]);
1177 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1178 // toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1179 // toc_table[track_num].lba_offset = max_logical_block;
1180 // toc_table[track_num].lba_size = 0;
1188 if(track_num > 0) is_cue = true;
1193 void SCSI_CDROM::open(const _TCHAR* file_path)
1195 _TCHAR img_file_path[_MAX_PATH];
1199 if(check_file_extension(file_path, _T(".cue"))) {
1203 open_cue_file(file_path);
1205 // get image file name
1206 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1207 if(!FILEIO::IsFileExisting(img_file_path)) {
1208 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
1209 if(!FILEIO::IsFileExisting(img_file_path)) {
1210 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1211 if(!FILEIO::IsFileExisting(img_file_path)) {
1212 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1213 if(!FILEIO::IsFileExisting(img_file_path)) {
1214 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin.gz"), get_file_path_without_extensiton(file_path));
1219 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1220 // get image file size
1221 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1223 FILEIO* fio = new FILEIO();
1224 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1225 char line[1024], *ptr;
1227 bool index0_found = false;
1228 bool index1_found = false;
1229 while(fio->Fgets(line, 1024) != NULL) {
1230 if(strstr(line, "FILE") != NULL) {
1232 } else if((ptr = strstr(line, "TRACK")) != NULL) {
1234 // "TRACK 02 MODE1/2352"
1236 while(*ptr == ' ' || *ptr == 0x09) {
1239 if((track = atoi(ptr)) > 0) {
1240 if(track > track_num) {
1243 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
1245 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
1246 // "PREGAP 00:02:00"
1248 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
1250 } else if((ptr = strstr(line, "INDEX")) != NULL) {
1251 // "INDEX 01 00:00:00"
1254 while(*ptr == ' ' || *ptr == 0x09) {
1257 int num = atoi(ptr);
1258 while(*ptr >= '0' && *ptr <= '9') {
1261 while(*ptr == ' ' || *ptr == 0x09) {
1265 index0_found = true;
1266 toc_table[track - 1].index0 = get_frames_from_msf(ptr);
1267 } else if(num == 1) {
1268 index1_found = true;
1269 toc_table[track - 1].index1 = get_frames_from_msf(ptr);
1274 if(track_num != 0) {
1275 for(int i = 1; i < track_num; i++) {
1276 if((toc_table[i].index0 == 0) && !(index0_found)) {
1277 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1278 } else if(toc_table[i].pregap == 0) {
1279 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1282 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1283 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1293 } else if(check_file_extension(file_path, _T(".ccd"))) {
1294 // get image file name
1295 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1296 if(!FILEIO::IsFileExisting(img_file_path)) {
1297 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1298 if(!FILEIO::IsFileExisting(img_file_path)) {
1299 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1302 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1305 // get image file size
1306 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1308 FILEIO* fio = new FILEIO();
1309 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1310 char line[1024], *ptr;
1312 while(fio->Fgets(line, 1024) != NULL) {
1313 if(strstr(line, "[Session ") != NULL) {
1315 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1316 if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1317 if(track > track_num) {
1321 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
1322 if(track > 0 && track < 0xa0) {
1323 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
1325 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1326 if(track > 0 && track < 0xa0) {
1327 toc_table[track - 1].pregap = atoi(ptr + 6);
1329 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1330 if(track > 0 && track < 0xa0) {
1331 toc_table[track - 1].index1 = atoi(ptr + 5);
1335 if(track_num != 0) {
1336 toc_table[0].lba_offset = 0;
1337 toc_table[0].pregap = 0;
1338 for(int i = 1; i < track_num; i++) {
1339 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1340 toc_table[i].lba_offset = toc_table[i].pregap;
1341 toc_table[i - 1].lba_size = toc_table[i].pregap - toc_table[i - 1].pregap;
1343 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1344 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1346 toc_table[track_num].lba_size = max_logical_block - toc_table[track_num - 1].lba_offset;
1348 toc_table[track_num].lba_size = 0;
1359 #ifdef _SCSI_DEBUG_LOG
1361 for(int i = 0; i < track_num + 1; i++) {
1362 uint32_t idx0_msf = lba_to_msf(toc_table[i].index0);
1363 uint32_t idx1_msf = lba_to_msf(toc_table[i].index1);
1364 uint32_t pgap_msf = lba_to_msf(toc_table[i].pregap);
1365 this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGap=%02x:%02x:%02x\n"), i + 1,
1366 (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
1367 (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
1368 (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
1374 void SCSI_CDROM::close()
1376 if(fio_img->IsOpened()) {
1379 memset(toc_table, 0, sizeof(toc_table));
1383 set_cdda_status(CDDA_OFF);
1386 bool SCSI_CDROM::mounted()
1388 if(is_cue) return true;
1389 return fio_img->IsOpened();
1392 bool SCSI_CDROM::accessed()
1394 bool value = access;
1399 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1401 if(cdda_status == CDDA_PLAYING) {
1402 if(mix_loop_num != 0) {
1403 int tmp_l = 0, tmp_r = 0;
1404 for(int i = 0; i < mix_loop_num; i++) {
1405 event_callback(EVENT_CDDA, 0);
1406 tmp_l += cdda_sample_l;
1407 tmp_r += cdda_sample_r;
1409 cdda_sample_l = tmp_l / mix_loop_num;
1410 cdda_sample_r = tmp_r / mix_loop_num;
1412 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
1413 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
1415 for(int i = 0; i < cnt; i++) {
1416 *buffer++ += val_l; // L
1417 *buffer++ += val_r; // R
1422 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1424 volume_l = decibel_to_volume(decibel_l + 3.0);
1425 volume_r = decibel_to_volume(decibel_r + 3.0);
1428 void SCSI_CDROM::set_volume(int volume)
1430 volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1433 #define STATE_VERSION 4
1435 // Q: If loading state when using another (saved) image? 20181013 K.O
1436 // May need close() and open() (or ...?).
1437 bool SCSI_CDROM::process_state(FILEIO* state_fio, bool loading)
1439 uint32_t offset = 0;
1441 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1444 if(!state_fio->StateCheckInt32(this_device_id)) {
1447 state_fio->StateValue(cdda_start_frame);
1448 state_fio->StateValue(cdda_end_frame);
1449 state_fio->StateValue(cdda_playing_frame);
1450 state_fio->StateValue(cdda_status);
1451 state_fio->StateValue(cdda_repeat);
1452 state_fio->StateValue(cdda_interrupt);
1453 state_fio->StateArray(cdda_buffer, sizeof(cdda_buffer), 1);
1454 state_fio->StateValue(cdda_buffer_ptr);
1455 state_fio->StateValue(cdda_sample_l);
1456 state_fio->StateValue(cdda_sample_r);
1457 state_fio->StateValue(event_cdda);
1458 state_fio->StateValue(event_cdda_delay_play);
1459 state_fio->StateValue(event_delay_interrupt);
1460 state_fio->StateValue(read_sectors);
1461 // state_fio->StateValue(mix_loop_num);
1462 state_fio->StateValue(read_mode);
1463 state_fio->StateValue(volume_m);
1465 offset = state_fio->FgetUint32_LE();
1467 if(fio_img->IsOpened()) {
1468 offset = fio_img->Ftell();
1470 state_fio->FputUint32_LE(offset);
1473 state_fio->StateValue(is_cue);
1474 state_fio->StateValue(current_track);
1475 // ToDo: Re-Open Image.20181118 K.O
1477 if(loading && fio_img->IsOpened()) {
1478 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1480 return SCSI_DEV::process_state(state_fio, loading);