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)) {
96 case SIG_ADPCM_CMD_REG:
103 void ADPCM::reset_adpcm()
106 // reset ADPCM hardware
107 read_ptr = write_ptr = 0;
108 read_buf = write_buf = 0;
110 msm_ptr = half_addr = 0;
119 reg_0b = 0x00; // OK?
121 adpcm_stream = false;
122 adpcm_repeat = false;
124 play_in_progress = false;
125 adpcm_paused = false;
127 if(event_fader != -1) {
128 cancel_event(this, event_fader);
131 if(event_ack != -1) {
132 cancel_event(this, event_ack);
136 reg_0c |= ADPCM_STOP_FLAG;
137 reg_0c &= ~ADPCM_PLAY_FLAG;
140 set_dma_status(false);
144 //d_msm->change_clock_w((ADPCM_CLOCK / 6) / adpcm_clock_divider); // From mednafen 1.22.1
145 //adpcm_volume = 0.0;
146 d_msm->set_volume((int)adpcm_volume);
147 //memset(ram, 0x00, sizeof(ram));
149 out_debug_log(_T("RESET ADPCM\n"));
150 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00, 0xffffffff);
151 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00, 0xffffffff);
155 void ADPCM::do_play()
157 reg_0c &= ~ADPCM_STOP_FLAG;
158 reg_0c |= ADPCM_PLAY_FLAG;
159 play_in_progress = true;
160 adpcm_paused = false;
163 void ADPCM::do_pause(bool pause)
165 //if(!(play_in_progress)) return;
167 reg_0c |= ADPCM_STOP_FLAG;
168 reg_0c &= ~ADPCM_PLAY_FLAG;
169 msm_last_cmd &= ~0x60;
172 out_debug_log(_T("ADPCM PAUSE PLAY PTR=%04x\n"), msm_ptr);
175 adpcm_paused = false;
177 reg_0c &= ~ADPCM_STOP_FLAG;
178 reg_0c |= ADPCM_PLAY_FLAG;
180 out_debug_log(_T("ADPCM UNPAUSE PLAY PTR=%04x\n"), msm_ptr);
184 void ADPCM::do_stop(bool do_notify)
187 reg_0c |= ADPCM_STOP_FLAG;
188 reg_0c &= ~ADPCM_PLAY_FLAG;
189 msm_last_cmd &= ~0x60;
190 play_in_progress = false;
191 if(do_notify) set_dma_status(false);
192 out_debug_log(_T("ADPCM STOP PLAY PTR=%04x DMA CLEAR=%s\n"), msm_ptr, (do_notify) ? _T("YES") : _T("NO"));
196 void ADPCM::set_dma_status(bool flag)
199 d_pce->write_signal(SIG_PCE_ADPCM_DMA, (flag) ? 0xffffffff : 0x00000000, 0xffffffff);
202 void ADPCM::write_signal(int ch, uint32_t data, uint32_t mask)
204 bool flag = ((data & mask) != 0);
205 //if(ch != SIG_ADPCM_VCLK) out_debug_log(_T("WRITE_SIGNAL SIG=%d DATA=%04x MASK=%04x\n"), ch, data, mask);
207 case SIG_ADPCM_DMACTRL:
208 if((data & 0x03) != 0) {
209 set_dma_status(true);
214 case SIG_ADPCM_PAUSE:
217 case SIG_ADPCM_DMA_ENABLED:
218 set_dma_status(flag);
219 if((flag)/* && (flag != dma_enabled)*/) {
220 reg_0c |= ADPCM_REMAIN_WRITE_BUF;
221 if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
222 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
223 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);
225 out_debug_log(_T("Start DMA port $0B/WAIT FOR DATA\n"));
229 case SIG_ADPCM_RESET:
234 case SIG_ADPCM_COMMAND: // REG $0D
237 case SIG_ADPCM_WRITE_DMA_DATA:
240 case SIG_ADPCM_DO_DMA_TRANSFER:
241 if((play_in_progress) && ((write_ptr & 0xffff) >= (msm_ptr & 0xffff))) {
242 // now streaming, wait dma not to overwrite buffer before it is played
243 reg_0b = 0x00;// From Ootake v2.38.
244 //set_dma_status(false); // DON'T MODIFY PCE's DMA STATUS (HACK by K.O 20190212)
246 set_dma_status(true);
247 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
250 case SIG_ADPCM_FORCE_DMA_TRANSFER:
252 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
255 case SIG_ADPCM_ADDR_HI: // REG $09
256 if((msm_last_cmd & 0x80) == 0) {
258 if((msm_last_cmd & 0x10) != 0) {
260 out_debug_log(_T("SET ADDRESS REGISTER HIGH ; UPDATE LENGTH TO %04x\n"), adpcm_length);
264 case SIG_ADPCM_ADDR_LO: // REG $08
265 if((msm_last_cmd & 0x80) == 0) {
267 if((msm_last_cmd & 0x10) != 0) {
269 out_debug_log(_T("SET ADDRESS REGISTER LOW ; UPDATE LENGTH TO %04x\n"), adpcm_length);
277 // Don't need to modify FLAGS 20190212 K.O
279 //reg_0c |= ADPCM_REMAIN_WRITE_BUF;
281 //if(write_buf == 0) {
282 // reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
285 //reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
286 ram[write_ptr & 0xffff] = data;
287 write_ptr = (write_ptr + 1) & 0xffff;
288 //if(msm_length < 0x10000) msm_length++;
292 case SIG_ADPCM_FADE_IN:
295 case SIG_ADPCM_FADE_OUT:
298 case SIG_ADPCM_SET_DIVIDER:
299 adpcm_clock_divider = 0x10 - (data & 0x0f);
300 d_msm->change_clock_w((ADPCM_CLOCK / 6) / adpcm_clock_divider);
302 case SIG_ADPCM_CLEAR_ACK:
303 reg_0b &= 0xfc; // Clear status register.
308 void ADPCM::update_length()
310 adpcm_length = (uint32_t)(addr_reg.w) & 0xffff;
311 msm_length = adpcm_length;
312 out_debug_log(_T("ADPCM SET LENGTH TO %04x\n"), adpcm_length);
315 void ADPCM::do_cmd(uint8_t cmd)
318 //out_debug_log(_T("ADPCM CMD=%02x\n"), cmd);
319 if(((cmd & 0x80) != 0) && ((msm_last_cmd & 0x80) == 0)) {
322 //msm_init(); // SAMPLE = 0x800, SSIS=0
323 out_debug_log(_T("ADPCM CMD RESET\n"));
324 //msm_last_cmd = cmd;
328 if(((cmd & 0x08) != 0) && ((msm_last_cmd & 0x08) == 0)) {
329 // ADPCM set read address
330 read_ptr = (uint32_t)(addr_reg.w) & 0xffff;
331 read_buf = ((cmd & 0x04) == 0) ? 2 : 1;
333 msm_ptr = ((cmd & 0x04) == 0) ? ((msm_ptr - 1) & 0xffff) : msm_ptr;
334 out_debug_log(_T("ADPCM SET READ ADDRESS ADDR=%04x BUF=%01x \n"), read_ptr, read_buf);
336 if(((cmd & 0x10) != 0) /*&& ((msm_last_cmd & 0x10) == 0)*/){
340 if(((cmd & 0x02) != 0) && ((msm_last_cmd & 0x02) == 0)) {
341 // ADPCM set write address
342 write_ptr = (uint32_t)(addr_reg.w) & 0xffff;
343 write_buf = ((cmd & 0x01) == 0) ? 1 : 0;
344 //write_ptr = (write_ptr - write_buf) & 0xffff;
345 //written_size = 0; // OK?
348 if((cmd & 0x10) != 0) {
349 // It's ugly... (;_;)
350 uint32_t _clk = (ADPCM_CLOCK / 6) / adpcm_clock_divider;
351 if(((read_ptr & 0xffff) >= 0x4000) &&
352 ((write_ptr & 0xffff) == 0x0000) &&
353 (adpcm_length != 0x8000) &&
354 (adpcm_length != 0xffff) &&
356 adpcm_length = adpcm_length & 0x7fff;
358 msm_length = adpcm_length;
359 out_debug_log(_T("ADPCM SET LENGTH LENGTH=%04x\n"), adpcm_length);
361 if(((cmd & 0x02) != 0) && ((msm_last_cmd & 0x02) == 0)) {
362 if((write_ptr == 0) || (write_ptr == 0x8000) || ((write_ptr & 0x1fff) == 0x1fff)) {
363 if((((read_ptr + adpcm_length) & 0x1fff) == 0x1fff) ||
364 ((read_ptr == 0) && (adpcm_length == 0x8000))) {
368 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"));
370 //bool req_play = false;
371 bool req_play = play_in_progress;
372 adpcm_repeat = ((cmd & 0x20) != 0) ? true : false;
374 if((play_in_progress) && !(adpcm_stream) && !(adpcm_repeat)) {
377 if(!(play_in_progress) && (adpcm_repeat)) {
380 if((cmd & 0x40) != 0) {
384 if(written_size > 0x8000) {
388 return; // Exit from command. 20190212 K.O
393 // msm_start_addr = (adpcm_read_ptr) & 0xffff;
394 if(((cmd & 0x40) != 0) /*&& !(adpcm_play_in_progress)*/) {
396 half_addr = (read_ptr + ((adpcm_length + 1) >> 1)) & 0xffff;
398 //written_size = 0; // OK?
400 play_in_progress = true;
403 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"));
405 // 20181213 K.O: Import from Ootake v2.83.Thanks to developers of Ootake.
406 if(((adpcm_length & 0xffff) >= 0x8000) && ((adpcm_length & 0xffff) <= 0x80ff)) {
407 half_addr = (read_ptr + 0x85) & 0xffff;
409 half_addr = (read_ptr + ((adpcm_length + 1) >> 1)) & 0xffff;
411 out_debug_log(_T("ADPCM UPDATE HALF ADDRESS HALF=%04x\n"), half_addr);
415 //set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
416 // set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
417 adpcm_stream = false;
418 adpcm_repeat = false;
419 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
420 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0xffffffff, 0xffffffff);
422 out_debug_log(_T("ADPCM STATUS UPDATE PLAY=%s\n"), (play_in_progress) ? _T("YES") : _T("NO"));
428 void ADPCM::msm_init()
435 void ADPCM::do_vclk(bool flag)
439 if((play_in_progress) && !(adpcm_paused)) {
440 //if(!((adpcm_length == 1) && ((msm_nibble & 1) == 0))) {
441 msm_data = (msm_nibble != 0) ? (ram[msm_ptr & 0xffff] & 0x0f) : ((ram[msm_ptr & 0xffff] & 0xf0) >> 4);
443 d_msm->data_w(msm_data);
445 bool need_wait =false;
446 if((msm_nibble == 0)) {
447 if((written_size > 0) /*&& !(adpcm_stopped)*/) written_size--;
448 // Increment pointers.
449 // 20181213 K.O: Re-order sequence from Ootake v2.83.Thanks to developers of Ootake.
450 if((msm_length == 0) && ((msm_last_cmd & 0x10) == 0)) {
451 if((adpcm_repeat) && ((adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff))) {
455 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
456 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0xffffffff, 0xffffffff);
457 if((msm_last_cmd & 0x40) != 0) {
458 do_stop(true); // true?
461 adpcm_stream = false;
462 adpcm_repeat = false;
469 msm_ptr = msm_ptr & 0xffff;
471 if(msm_length > 0) msm_length--;
472 if(msm_length >= 0x10000) msm_length = 0xffff;
473 if((adpcm_repeat) && (adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff)) {
474 if(msm_ptr == (half_addr & 0xffff)) {
475 half_addr = half_addr + 0x85;
476 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
477 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
479 } else if(adpcm_length < 0x7fff) {
480 if(msm_ptr == (half_addr & 0xffff)) {
481 half_addr = half_addr + ((adpcm_length - 1024) & 0xffff);
482 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
483 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
485 } else if((msm_ptr == 0x8000) || (msm_ptr == 0x0000)) {
486 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0xffffffff, 0xffffffff);
487 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0x00000000, 0xffffffff);
488 } /*else if((msm_length == 0) && ((msm_last_cmd & 0x10) == 0)){
489 d_pce->write_signal(SIG_PCE_ADPCM_HALF, 0x00000000, 0xffffffff);
490 d_pce->write_signal(SIG_PCE_ADPCM_FULL, 0xffffffff, 0xffffffff);
491 if((msm_last_cmd & 0x40) != 0) {
492 do_stop(true); // true?
494 adpcm_stream = false;
495 adpcm_repeat = false;
503 if((write_ptr & 0xffff) < (msm_ptr & 0xffff)) {
504 if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
505 //do_pause(false); // Unpause if paused && data in.
506 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
512 } else { // nibble = 1
515 if((dma_enabled) && (play_in_progress)) {
516 if(d_pce->read_signal(SIG_PCE_CDROM_DATA_IN) != 0) {
517 //do_pause(false); // Unpause if paused && data in.
518 do_dma(d_pce->read_signal(SIG_PCE_CDROM_RAW_DATA));
526 bool ADPCM::do_dma(uint8_t data)
528 ram[write_ptr & 0xffff] = data;
529 write_ptr = (write_ptr + 1) & 0xffff;
530 written_size = (written_size + 1) & 0xffff;;
531 //if(msm_length < 0x10000) msm_length++;
534 reg_0c &= ~ADPCM_REMAIN_WRITE_BUF;
539 void ADPCM::set_ack(int clocks)
541 if(event_ack != -1) cancel_event(this, event_ack);
544 d_pce->write_signal(SIG_PCE_CDROM_SET_ACK, 0xff, 0xff);
546 double us = (((double)clocks) * 1.0e6) / ((double)CPU_CLOCKS);
547 register_event(this, EVENT_SET_ACK, us, false, &event_ack);
551 void ADPCM::clear_ack(int clocks)
553 if(event_ack != -1) cancel_event(this, event_ack);
556 d_pce->write_signal(SIG_PCE_CDROM_CLEAR_ACK, 0xff, 0xff);
558 double us = (((double)clocks) * 1.0e6) / ((double)CPU_CLOCKS);
559 register_event(this, EVENT_CLEAR_ACK, us, false, &event_ack);
563 void ADPCM::fade_in(int usec)
565 if(event_fader != -1) {
566 cancel_event(this, event_fader);
568 register_event(this, EVENT_FADE_IN, (double)usec, true, &event_fader);
570 d_msm->set_volume((int)adpcm_volume);
573 void ADPCM::fade_out(int usec)
575 if(event_fader != -1) {
576 cancel_event(this, event_fader);
578 register_event(this, EVENT_FADE_OUT, (double)usec, true, &event_fader);
579 adpcm_volume = 100.0;
580 d_msm->set_volume((int)adpcm_volume);
583 void ADPCM::event_callback(int id, int err)
586 case EVENT_CLEAR_ACK:
588 d_pce->write_signal(SIG_PCE_CDROM_CLEAR_ACK, 0xff, 0xff);
592 d_pce->write_signal(SIG_PCE_CDROM_SET_ACK, 0xff, 0xff);
595 if((adpcm_volume += 0.1) >= 100.0) {
596 cancel_event(this, event_fader);
598 adpcm_volume = 100.0;
600 d_msm->set_volume((int)adpcm_volume);
603 if((adpcm_volume -= 0.1) <= 0.0) {
604 cancel_event(this, event_fader);
608 d_msm->set_volume((int)adpcm_volume);
613 void ADPCM::mix(int32_t* buffer, int cnt)
615 d_msm->mix(buffer, cnt);
618 #define STATE_VERSION 7
620 bool ADPCM::process_state(FILEIO* state_fio, bool loading)
622 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
625 if(!state_fio->StateCheckInt32(this_device_id)) {
628 state_fio->StateValue(addr_reg);
629 state_fio->StateValue(reg_0b);
630 state_fio->StateValue(reg_0c);
631 state_fio->StateValue(msm_last_cmd);
633 state_fio->StateBuffer(ram, sizeof(ram), 1);
635 state_fio->StateValue(read_ptr);
636 state_fio->StateValue(read_buf);
637 state_fio->StateValue(write_ptr);
638 state_fio->StateValue(write_buf);
640 state_fio->StateValue(msm_data);
641 state_fio->StateValue(msm_ptr);
642 state_fio->StateValue(msm_nibble);
643 state_fio->StateValue(msm_length);
644 state_fio->StateValue(half_addr);
645 state_fio->StateValue(adpcm_length);
647 state_fio->StateValue(written_size);
648 state_fio->StateValue(dma_enabled);
649 state_fio->StateValue(play_in_progress);
650 state_fio->StateValue(adpcm_paused);
651 state_fio->StateValue(adpcm_stream);
652 state_fio->StateValue(adpcm_repeat);
653 state_fio->StateValue(adpcm_volume);
654 state_fio->StateValue(event_fader);
655 state_fio->StateValue(event_ack);