2 NEC-HE PC Engine Emulator 'ePCEngine'
3 SHARP X1twin Emulator 'eX1twin'
5 Origin : Ootake (joypad/cdrom)
7 : MESS (vdc/vce/vpc/cdrom)
8 Author : Takeda.Toshiya
10 Author : Kyuma.Ohta <whatisthis.sowhat _at_ gmail.com>
11 Date : 2019.02.09- Split from pce.cpp
13 [ PC-Engine around ADPCM]
20 #include "../msm5205.h"
21 #include "../scsi_host.h"
22 #include "../scsi_cdrom.h"
27 #define ADPCM_REMAIN_READ_BUF 0x80
28 #define ADPCM_REMAIN_WRITE_BUF 0x04
29 #define ADPCM_PLAY_FLAG 0x08
30 #define ADPCM_STOP_FLAG 0x01
32 #define EVENT_CLEAR_ACK 1
33 #define EVENT_SET_ACK 2
34 #define EVENT_FADE_IN 3
35 #define EVENT_FADE_OUT 4
37 void ADPCM::initialize()
39 adpcm_clock_divider = 1; // OK?
47 memset(ram, 0x00, sizeof(ram));
57 uint32_t ADPCM::read_signal(int ch)
62 // Don't need to modify FLAGS 20190212 K.O
63 // reg_0c |= ADPCM_REMAIN_READ_BUF;
65 // if(read_buf == 0) {
66 // reg_0c &= ~ADPCM_REMAIN_READ_BUF;
70 // reg_0c &= ~ADPCM_REMAIN_READ_BUF;
71 uint8_t _d = ram[read_ptr & 0xffff];
72 read_ptr = (read_ptr + 1) & 0xffff;
76 case SIG_ADPCM_DMACTRL:
79 case SIG_ADPCM_PLAY_IN_PROGRESS:
80 return ((play_in_progress) ? 0xffffffff : 0);
82 case SIG_ADPCM_STATUS_REG:
84 uint8_t data = reg_0c;
85 // Hack from Ootake v2.83.
86 if((play_in_progress)) {
93 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
97 case SIG_ADPCM_CMD_REG:
104 void ADPCM::reset_adpcm()
107 // reset ADPCM hardware
108 read_ptr = write_ptr = 0;
109 read_buf = write_buf = 0;
111 msm_ptr = half_addr = 0;
120 reg_0b = 0x00; // OK?
122 adpcm_stream = false;
123 adpcm_repeat = false;
125 dma_connected = false;
126 play_in_progress = false;
127 adpcm_paused = false;
129 if(event_fader != -1) {
130 cancel_event(this, event_fader);
133 if(event_ack != -1) {
134 cancel_event(this, event_ack);
138 reg_0c |= ADPCM_STOP_FLAG;
139 reg_0c &= ~ADPCM_PLAY_FLAG;
142 set_dma_status(false);
146 //d_msm->change_clock_w((ADPCM_CLOCK / 6) / adpcm_clock_divider); // From mednafen 1.22.1
147 //adpcm_volume = 0.0;
148 d_msm->set_volume((int)adpcm_volume);
149 //memset(ram, 0x00, sizeof(ram));
151 out_debug_log(_T("RESET ADPCM\n"));
152 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00, 0xffffffff);
153 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00, 0xffffffff);
157 void ADPCM::do_play()
159 reg_0c &= ~ADPCM_STOP_FLAG;
160 reg_0c |= ADPCM_PLAY_FLAG;
161 play_in_progress = true;
162 adpcm_paused = false;
165 void ADPCM::do_pause(bool pause)
167 //if(!(play_in_progress)) return;
169 reg_0c |= ADPCM_STOP_FLAG;
170 reg_0c &= ~ADPCM_PLAY_FLAG;
171 msm_last_cmd &= ~0x60;
174 out_debug_log(_T("ADPCM PAUSE PLAY PTR=%04x\n"), msm_ptr);
177 adpcm_paused = false;
179 reg_0c &= ~ADPCM_STOP_FLAG;
180 reg_0c |= ADPCM_PLAY_FLAG;
182 out_debug_log(_T("ADPCM UNPAUSE PLAY PTR=%04x\n"), msm_ptr);
186 void ADPCM::do_stop(bool do_notify)
189 reg_0c |= ADPCM_STOP_FLAG;
190 reg_0c &= ~ADPCM_PLAY_FLAG;
191 msm_last_cmd &= ~0x60;
192 play_in_progress = false;
193 if(do_notify) set_dma_status(false);
194 out_debug_log(_T("ADPCM STOP PLAY PTR=%04x DMA CLEAR=%s\n"), msm_ptr, (do_notify) ? _T("YES") : _T("NO"));
198 void ADPCM::set_dma_status(bool flag)
202 dma_connected = false;
204 d_pce->write_signal(SIG_PCE_ADPCM_DMA, (flag) ? 0xffffffff : 0x00000000, 0xffffffff);
207 void ADPCM::write_signal(int ch, uint32_t data, uint32_t mask)
209 bool flag = ((data & mask) != 0);
210 //if(ch != SIG_ADPCM_VCLK) out_debug_log(_T("WRITE_SIGNAL SIG=%d DATA=%04x MASK=%04x\n"), ch, data, mask);
212 case SIG_ADPCM_DMACTRL:
213 if((data & 0x03) != 0) {
214 set_dma_status(true);
219 case SIG_ADPCM_PAUSE:
222 case SIG_ADPCM_DMA_ENABLED:
223 set_dma_status(flag);
224 if((flag)/* && (flag != dma_enabled)*/) {
225 dma_connected = true;
226 reg_0c |= ADPCM_REMAIN_WRITE_BUF;
227 if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
228 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
229 out_debug_log(_T("Start DMA port $0B/ALREADY READ DATA ADPCM_WRITE_PTR=%04x ADPCM_READ_PTR=%04x MSM_START_ADDR=%04x\n"),write_ptr, read_ptr, msm_ptr);
231 out_debug_log(_T("Start DMA port $0B/WAIT FOR DATA\n"));
235 case SIG_ADPCM_RESET:
240 case SIG_ADPCM_COMMAND: // REG $0D
243 case SIG_ADPCM_WRITE_DMA_DATA:
246 case SIG_ADPCM_DO_DMA_TRANSFER:
247 //if((play_in_progress) && ((write_ptr & 0xffff) >= (msm_ptr & 0xffff))) {
248 // // now streaming, wait dma not to overwrite buffer before it is played
249 // reg_0b = 0x00;// From Ootake v2.38.
250 // dma_connected = false;
251 //set_dma_status(false); // DON'T MODIFY PCE's DMA STATUS (HACK by K.O 20190212)
253 //set_dma_status(true);
254 dma_connected = true;
255 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
258 case SIG_ADPCM_FORCE_DMA_TRANSFER:
260 dma_connected = true;
261 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
264 case SIG_ADPCM_DMA_RELEASED:
266 dma_connected = false;
268 case SIG_ADPCM_ADDR_HI: // REG $09
269 if((msm_last_cmd & 0x80) == 0) {
271 if((msm_last_cmd & 0x10) != 0) {
273 out_debug_log(_T("SET ADDRESS REGISTER HIGH ; UPDATE LENGTH TO %04x\n"), adpcm_length);
277 case SIG_ADPCM_ADDR_LO: // REG $08
278 if((msm_last_cmd & 0x80) == 0) {
280 if((msm_last_cmd & 0x10) != 0) {
282 out_debug_log(_T("SET ADDRESS REGISTER LOW ; UPDATE LENGTH TO %04x\n"), adpcm_length);
290 // Don't need to modify FLAGS 20190212 K.O
292 //reg_0c |= ADPCM_REMAIN_WRITE_BUF;
294 //if(write_buf == 0) {
295 // reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
298 //reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
299 ram[write_ptr & 0xffff] = data;
300 write_ptr = (write_ptr + 1) & 0xffff;
301 written_size = written_size + 1;
305 case SIG_ADPCM_FADE_IN:
308 case SIG_ADPCM_FADE_OUT:
311 case SIG_ADPCM_SET_DIVIDER:
312 adpcm_clock_divider = 0x10 - (data & 0x0f);
313 d_msm->change_clock_w((ADPCM_CLOCK / 6) / adpcm_clock_divider);
315 case SIG_ADPCM_CLEAR_ACK:
316 reg_0b &= 0xfc; // Clear status register.
321 void ADPCM::update_length()
323 adpcm_length = (uint32_t)(addr_reg.w) & 0xffff;
324 msm_length = adpcm_length;
325 out_debug_log(_T("ADPCM SET LENGTH TO %04x\n"), adpcm_length);
328 void ADPCM::do_cmd(uint8_t cmd)
331 //out_debug_log(_T("ADPCM CMD=%02x\n"), cmd);
332 if(((cmd & 0x80) != 0) && ((msm_last_cmd & 0x80) == 0)) {
335 //msm_init(); // SAMPLE = 0x800, SSIS=0
336 out_debug_log(_T("ADPCM CMD RESET\n"));
337 //msm_last_cmd = cmd;
341 if(((cmd & 0x08) != 0) && ((msm_last_cmd & 0x08) == 0)) {
342 // ADPCM set read address
343 read_ptr = (uint32_t)(addr_reg.w) & 0xffff;
344 read_buf = ((cmd & 0x04) == 0) ? 2 : 1;
346 msm_ptr = ((cmd & 0x04) == 0) ? ((msm_ptr - 1) & 0xffff) : msm_ptr;
347 out_debug_log(_T("ADPCM SET READ ADDRESS ADDR=%04x BUF=%01x \n"), read_ptr, read_buf);
349 if(((cmd & 0x10) != 0) /*&& ((msm_last_cmd & 0x10) == 0)*/){
353 if(((cmd & 0x02) != 0) && ((msm_last_cmd & 0x02) == 0)) {
354 // ADPCM set write address
355 write_ptr = (uint32_t)(addr_reg.w) & 0xffff;
356 write_buf = ((cmd & 0x01) == 0) ? 1 : 0;
357 //write_ptr = (write_ptr - write_buf) & 0xffff;
358 //written_size = 0; // OK?
361 if((cmd & 0x10) != 0) {
362 // It's ugly... (;_;)
363 uint32_t _clk = (ADPCM_CLOCK / 6) / adpcm_clock_divider;
364 if(((read_ptr & 0xffff) >= 0x4000) &&
365 ((write_ptr & 0xffff) == 0x0000) &&
366 (adpcm_length != 0x8000) &&
367 (adpcm_length != 0xffff) &&
369 adpcm_length = adpcm_length & 0x7fff;
371 out_debug_log(_T("ADPCM SET LENGTH LENGTH=%04x\n"), adpcm_length);
373 if(((cmd & 0x02) != 0) && ((msm_last_cmd & 0x02) == 0)) {
374 if((write_ptr == 0) || (write_ptr == 0x8000) || ((write_ptr & 0x1fff) == 0x1fff)) {
375 if((((read_ptr + adpcm_length) & 0x1fff) == 0x1fff) ||
376 ((read_ptr == 0) && (adpcm_length == 0x8000))) {
380 out_debug_log(_T("ADPCM SET WRITE ADDRESS ADDR=%04x BUF=%01x STREAM=%s\n"), write_ptr, write_buf, (adpcm_stream) ? _T("YES") : _T("NO"));
382 //bool req_play = false;
383 bool req_play = play_in_progress;
384 adpcm_repeat = ((cmd & 0x20) != 0) ? true : false;
386 if((play_in_progress) && !(adpcm_stream) && !(adpcm_repeat)) {
389 if(!(play_in_progress) && (adpcm_repeat)) {
392 if((cmd & 0x40) != 0) {
396 if(written_size > 0x8000) {
400 return; // Exit from command. 20190212 K.O
405 // msm_start_addr = (adpcm_read_ptr) & 0xffff;
406 if(((cmd & 0x40) != 0) /*&& !(adpcm_play_in_progress)*/) {
408 half_addr = (read_ptr + ((adpcm_length + 1) >> 1)) & 0xffff;
410 if(!(play_in_progress)) written_size = 0; // OK?
412 play_in_progress = true;
413 msm_length = adpcm_length; // OK?
416 out_debug_log(_T("ADPCM START PLAY(%s) START=%04x LENGTH=%04x HALF=%04x STREAM=%s\n"), (dma_enabled) ? _T("DMA") : _T("PIO"), msm_ptr, msm_length, half_addr, (adpcm_stream) ? _T("YES") : _T("NO"));
418 // 20181213 K.O: Import from Ootake v2.83.Thanks to developers of Ootake.
419 if(((adpcm_length & 0xffff) >= 0x8000) && ((adpcm_length & 0xffff) <= 0x80ff)) {
420 half_addr = (read_ptr + 0x85) & 0xffff;
422 half_addr = (read_ptr + ((adpcm_length + 1) >> 1)) & 0xffff;
424 out_debug_log(_T("ADPCM UPDATE HALF ADDRESS HALF=%04x\n"), half_addr);
428 //set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
429 // set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
430 adpcm_stream = false;
431 adpcm_repeat = false;
432 //d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
433 //d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
434 do_stop(true); // false?
435 out_debug_log(_T("ADPCM STATUS UPDATE PLAY=%s\n"), (play_in_progress) ? _T("YES") : _T("NO"));
441 void ADPCM::msm_init()
448 void ADPCM::do_vclk(bool flag)
452 if((play_in_progress) && !(adpcm_paused)) {
453 msm_data = (msm_nibble != 0) ? (ram[msm_ptr & 0xffff] & 0x0f) : ((ram[msm_ptr & 0xffff] & 0xf0) >> 4);
455 d_msm->data_w(msm_data);
457 bool need_wait =false;
458 if((msm_nibble == 0)) {
459 // Increment pointers.
460 // 20181213 K.O: Re-order sequence from Ootake v2.83.Thanks to developers of Ootake.
461 if(((dma_enabled) && !(dma_connected)) || !(dma_enabled)) {
462 if((msm_length == 0) && ((msm_last_cmd & 0x10) == 0)) {
463 if((adpcm_repeat) && ((adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff))) {
468 //d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
469 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0xffffffff, 0xffffffff);
470 if((msm_last_cmd & 0x40) != 0) {
471 do_stop(false); // true?
474 adpcm_stream = false;
475 adpcm_repeat = false;
479 if((written_size > 0) /*&& !(adpcm_stopped)*/) written_size--;
485 msm_ptr = msm_ptr & 0xffff;
487 if(msm_length > 0) msm_length--;
488 if(msm_length < 0) msm_length = 0;
489 if(msm_length > 0x10000) msm_length = 0x10000;
490 if((adpcm_repeat) && (adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff)) {
491 if(msm_ptr == (half_addr & 0xffff)) {
492 half_addr = half_addr + 0x85;
493 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
494 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
496 } else if(adpcm_length < 0x7fff) {
497 if(msm_ptr == (half_addr & 0xffff)) {
498 half_addr = half_addr + ((adpcm_length - 1024) & 0xffff);
499 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
500 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
502 } else if((msm_ptr == 0x8000) || (msm_ptr == 0x0000)) {
503 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
504 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
510 if(((write_ptr & 0xffff) < (msm_ptr & 0xffff)) ||
511 (written_size <= 0)) {
512 if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
513 //do_pause(false); // Unpause if paused && data in.
514 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
520 } else { // nibble = 1
523 // if((dma_enabled) && (play_in_progress) && (dma_connected)) {
524 // reg_0c |= ADPCM_REMAIN_WRITE_BUF;
525 // if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
526 // //do_pause(false); // Unpause if paused && data in.
527 // do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
535 bool ADPCM::do_dma(uint8_t data)
537 ram[write_ptr & 0xffff] = data;
538 write_ptr = (write_ptr + 1) & 0xffff;
539 if(written_size < 0) written_size = 0;
540 written_size = written_size + 1;
544 reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
549 void ADPCM::set_ack(int clocks)
551 if(event_ack != -1) cancel_event(this, event_ack);
554 d_pce->write_signal(SIG_PCE_CDROM_SET_ACK, 0xff, 0xff);
556 double us = (((double)clocks) * 1.0e6) / ((double)CPU_CLOCKS);
557 register_event(this, EVENT_SET_ACK, us, false, &event_ack);
561 void ADPCM::clear_ack(int clocks)
563 if(event_ack != -1) cancel_event(this, event_ack);
566 d_pce->write_signal(SIG_PCE_CDROM_CLEAR_ACK, 0xff, 0xff);
568 double us = (((double)clocks) * 1.0e6) / ((double)CPU_CLOCKS);
569 register_event(this, EVENT_CLEAR_ACK, us, false, &event_ack);
573 void ADPCM::fade_in(int usec)
575 if(event_fader != -1) {
576 cancel_event(this, event_fader);
578 register_event(this, EVENT_FADE_IN, (double)usec, true, &event_fader);
580 d_msm->set_volume((int)adpcm_volume);
583 void ADPCM::fade_out(int usec)
585 if(event_fader != -1) {
586 cancel_event(this, event_fader);
588 register_event(this, EVENT_FADE_OUT, (double)usec, true, &event_fader);
589 adpcm_volume = 100.0;
590 d_msm->set_volume((int)adpcm_volume);
593 void ADPCM::event_callback(int id, int err)
596 case EVENT_CLEAR_ACK:
598 d_pce->write_signal(SIG_PCE_CDROM_CLEAR_ACK, 0xff, 0xff);
602 d_pce->write_signal(SIG_PCE_CDROM_SET_ACK, 0xff, 0xff);
605 if((adpcm_volume += 0.1) >= 100.0) {
606 cancel_event(this, event_fader);
608 adpcm_volume = 100.0;
610 d_msm->set_volume((int)adpcm_volume);
613 if((adpcm_volume -= 0.1) <= 0.0) {
614 cancel_event(this, event_fader);
618 d_msm->set_volume((int)adpcm_volume);
623 void ADPCM::mix(int32_t* buffer, int cnt)
625 d_msm->mix(buffer, cnt);
628 #define STATE_VERSION 7
630 bool ADPCM::process_state(FILEIO* state_fio, bool loading)
632 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
635 if(!state_fio->StateCheckInt32(this_device_id)) {
638 state_fio->StateValue(addr_reg);
639 state_fio->StateValue(reg_0b);
640 state_fio->StateValue(reg_0c);
641 state_fio->StateValue(msm_last_cmd);
643 state_fio->StateBuffer(ram, sizeof(ram), 1);
645 state_fio->StateValue(read_ptr);
646 state_fio->StateValue(read_buf);
647 state_fio->StateValue(write_ptr);
648 state_fio->StateValue(write_buf);
650 state_fio->StateValue(msm_data);
651 state_fio->StateValue(msm_ptr);
652 state_fio->StateValue(msm_nibble);
653 state_fio->StateValue(msm_length);
654 state_fio->StateValue(half_addr);
655 state_fio->StateValue(adpcm_length);
657 state_fio->StateValue(written_size);
658 state_fio->StateValue(dma_enabled);
659 state_fio->StateValue(dma_connected);
660 state_fio->StateValue(play_in_progress);
661 state_fio->StateValue(adpcm_paused);
662 state_fio->StateValue(adpcm_stream);
663 state_fio->StateValue(adpcm_repeat);
664 state_fio->StateValue(adpcm_volume);
665 state_fio->StateValue(event_fader);
666 state_fio->StateValue(event_ack);