@@ -38,6 +38,9 @@ namespace {
3838
3939using namespace std ::literals;
4040
41+ // The buffer/decoder chip the PSX CPU will talk to is the CXD1199, which
42+ // datasheet can be found at https://archive.org/details/cxd-1199
43+
4144class CDRomImpl final : public PCSX::CDRom {
4245 enum Commands {
4346 CdlSync = 0 ,
@@ -104,14 +107,14 @@ class CDRomImpl final : public PCSX::CDRom {
104107 m_seed = 9223521712174600777ull ;
105108 m_dataFIFOIndex = 0 ;
106109 m_dataFIFOSize = 0 ;
107- m_registerIndex = 0 ;
110+ m_registerAddress = 0 ;
108111 m_currentPosition.reset ();
109112 m_seekPosition.reset ();
110113 m_speed = Speed::Simple;
111114 m_speedChanged = false ;
112115 m_status = Status::Idle;
113116 m_dataRequested = false ;
114- m_causeMask = 0x1f ;
117+ m_interruptCauseMask = 0x1f ;
115118 m_subheaderFilter = false ;
116119 m_realtime = false ;
117120 m_commandFifo.clear ();
@@ -279,7 +282,7 @@ class CDRomImpl final : public PCSX::CDRom {
279282 void maybeTriggerIRQ (Cause cause, QueueElement &element) {
280283 uint8_t causeValue = static_cast <uint8_t >(cause);
281284 uint8_t bit = 1 << (causeValue - 1 );
282- if (m_causeMask & bit) {
285+ if (m_interruptCauseMask & bit) {
283286 element.setValue (cause);
284287 bool actuallyTriggering = false ;
285288 if (maybeEnqueueResponse (element)) {
@@ -329,15 +332,49 @@ class CDRomImpl final : public PCSX::CDRom {
329332 }
330333
331334 uint8_t read0 () override {
332- uint8_t v01 = m_registerIndex & 3 ;
333- uint8_t adpcmPlaying = 0 ;
334- uint8_t v3 = m_commandFifo. isPayloadEmpty () ? 0x08 : 0 ;
335- uint8_t v4 = !m_commandFifo. isPayloadFull () ? 0x10 : 0 ;
336- uint8_t v5 = !m_responseFifo[ 0 ]. isPayloadEmpty () ? 0x20 : 0 ;
337- uint8_t v6 = m_dataFIFOSize != m_dataFIFOIndex ? 0x40 : 0 ;
335+ // HSTS (host status) register
336+ /*
337+ bit 7: BUSYSTS (busy status)
338+ This is high when the host writes a command into the command register and low when the sub
339+ CPU sets the CLRBUSY bit (bit 6) of the CLRCTL register.
340+ */
338341 uint8_t v7 = m_commandFifo.hasValue ? 0x80 : 0 ;
339-
340- uint8_t ret = v01 | adpcmPlaying | v3 | v4 | v5 | v6 | v7;
342+ /*
343+ bit 6: DRQSTS (data request status)
344+ Indicates to the host that the buffer memory data transfer request status is established. When
345+ transferring data in the I/O mode, the host should confirm that this bit is high before accessing the
346+ WRDATA or RDDATA register.
347+ */
348+ uint8_t v6 = m_dataFIFOSize != m_dataFIFOIndex ? 0x40 : 0 ;
349+ /*
350+ bit 5: RSLRRDY (result read ready)
351+ The result register is not empty when this bit is high. At this time, the host can read the result
352+ register.
353+ */
354+ uint8_t v5 = !m_responseFifo[0 ].isPayloadEmpty () ? 0x20 : 0 ;
355+ /*
356+ bit 4: PRMWRDY (parameter write ready)
357+ The PARAMETER register is not full when this bit is high. At this time, the host writes data into the
358+ PARAMETER register.
359+ */
360+ uint8_t v4 = !m_commandFifo.isPayloadFull () ? 0x10 : 0 ;
361+ /*
362+ bit 3: PRMEMPT (parameter empty)
363+ The PARAMETER register is empty when this bit is high.
364+ */
365+ uint8_t v3 = m_commandFifo.isPayloadEmpty () ? 0x08 : 0 ;
366+ /*
367+ bit 2: ADPBUSY (ADPCM busy)
368+ This bit is set high for ADPCM decoding.
369+ */
370+ uint8_t v2 = 0 ; /* adpcmPlaying */
371+ /*
372+ bits 1, 0: RA1, 0
373+ The values of the RA1 and 0 bits for the ADDRESS register can be read from these bits.
374+ */
375+ uint8_t v01 = m_registerAddress & 3 ;
376+
377+ uint8_t ret = v01 | v2 | v3 | v4 | v5 | v6 | v7;
341378 const bool debug = PCSX::g_emulator->settings .get <PCSX::Emulator::SettingDebugSettings>()
342379 .get <PCSX::Emulator::DebugSettings::LoggingHWCDROM>();
343380 if (debug) {
@@ -349,6 +386,7 @@ class CDRomImpl final : public PCSX::CDRom {
349386 }
350387
351388 uint8_t read1 () override {
389+ // RESULT
352390 uint8_t ret = m_responseFifo[0 ].readPayloadByte ();
353391 maybeScheduleNextCommand ();
354392
@@ -362,6 +400,7 @@ class CDRomImpl final : public PCSX::CDRom {
362400 }
363401
364402 uint8_t read2 () override {
403+ // RD DATA
365404 uint8_t ret = 0 ;
366405 if (!dataFIFOHasData ()) {
367406 ret = 0 ;
@@ -379,35 +418,53 @@ class CDRomImpl final : public PCSX::CDRom {
379418 }
380419
381420 uint8_t read3 () override {
421+ /*
422+ * bit 4: BFWRDY (buffer write ready)
423+ * The BFWRDY status is established if there is area where writing is possible in the buffer of 1 sector
424+ * or more for sound map playback. It is established in any of the following cases:
425+ * (1) When the host has set the SMEN bit (bit 5) of the HCHPCTL register high
426+ * (2) When there is sound map data area of 1 sector or more in the buffer memory (when the buffer
427+ * is not full) after the sound map data equivalent to 1 sector from the host has been written into
428+ * the buffer memory
429+ * (3) When an area for writing the sound map data has been created in the buffer memory by the
430+ * completion of the sound map ADPCM decoding of one sector
431+ * bit 3: BFEMPT (buffer empty)
432+ * The BFEMPT status is established when there is no more sector data in the buffer memory upon
433+ * completion of the sound map ADPCM decoding of one sector for sound map playback.
434+ * bits 2 to 0: INTSTS#2 to 0
435+ * The values of these bits are those of the corresponding bits for the sub CPU HIFCTL register.
436+ */
437+
382438 uint8_t ret = 0 ;
383- switch (m_registerIndex & 1 ) {
439+ switch (m_registerAddress & 1 ) {
384440 case 0 : {
385- ret = m_causeMask | 0xe0 ;
441+ // HINT MSK (host interrupt mask)
442+ ret = m_interruptCauseMask;
386443 } break ;
387444 case 1 : {
388- // cause
389- // TODO: add bit 4
390- ret = m_responseFifo[0 ].getValue () | 0xe0 ;
445+ // HINT STS (host interrupt status)
446+ ret = m_responseFifo[0 ].getValue ();
391447 } break ;
392448 }
393449 const bool debug = PCSX::g_emulator->settings .get <PCSX::Emulator::SettingDebugSettings>()
394450 .get <PCSX::Emulator::DebugSettings::LoggingHWCDROM>();
395451 if (debug) {
396452 auto ®s = PCSX::g_emulator->m_cpu ->m_regs ;
397453 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: %08x.%08x] r3.%i: %02x\n " , regs.pc , regs.cycle ,
398- m_registerIndex & 1 , ret);
454+ m_registerAddress & 1 , ret);
399455 }
400- return ret;
456+ return ret | 0xe0 ;
401457 }
402458
403459 void write0 (uint8_t value) override {
460+ // ADDRESS
404461 const bool debug = PCSX::g_emulator->settings .get <PCSX::Emulator::SettingDebugSettings>()
405462 .get <PCSX::Emulator::DebugSettings::LoggingHWCDROM>();
406463 if (debug) {
407464 auto ®s = PCSX::g_emulator->m_cpu ->m_regs ;
408465 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: %08x.%08x] w0: %02x\n " , regs.pc , regs.cycle , value);
409466 }
410- m_registerIndex = value & 3 ;
467+ m_registerAddress = value & 3 ;
411468 }
412469
413470 void write1 (uint8_t value) override {
@@ -416,10 +473,11 @@ class CDRomImpl final : public PCSX::CDRom {
416473 if (debug) {
417474 auto ®s = PCSX::g_emulator->m_cpu ->m_regs ;
418475 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: %08x.%08x] w1.%i: %02x\n " , regs.pc , regs.cycle ,
419- m_registerIndex , value);
476+ m_registerAddress , value);
420477 }
421- switch (m_registerIndex ) {
478+ switch (m_registerAddress ) {
422479 case 0 :
480+ // COMMAND
423481 m_commandFifo.value = value;
424482 if (!m_commandFifo.hasValue ) {
425483 if (PCSX::g_emulator->settings .get <PCSX::Emulator::SettingDebugSettings>()
@@ -431,18 +489,35 @@ class CDRomImpl final : public PCSX::CDRom {
431489 m_commandFifo.hasValue = true ;
432490 break ;
433491 case 1 : {
434- // ??
492+ // WR DATA
435493 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: w1:1 not available yet\n " );
436494 PCSX::g_system->pause ();
437495 } break ;
438496 case 2 : {
439- // ??
497+ /*
498+ CI (coding information)
499+ This sets the coding information for sound map playback. The bit allocation is the same as that for the
500+ coding information bytes of the sub header.
501+ bits 7, 5, 3, 1: Reserved
502+ bit 6: EMPHASIS
503+ High: Emphasis ON
504+ Low : Emphasis OFF
505+ bit 4: BITLNGTH
506+ High: 8 bits
507+ Low : 4 bits
508+ bit 2: FS
509+ High: 18.9 kHz
510+ Low : 37.8 kHz
511+ bit 0: S/M (stereo/mono)
512+ High: Stereo
513+ Low : Mono
514+ */
440515 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: w1:2 not available yet\n " );
441516 PCSX::g_system->pause ();
442517 } break ;
443518 case 3 : {
444- // Volume setting RR
445- PCSX::g_system-> log (PCSX::LogClass::CDROM, " CD-Rom: w1:3 not available yet \n " ) ;
519+ // ATV2 Right-to-Right
520+ m_atv[ 2 ] = value ;
446521 } break ;
447522 }
448523 }
@@ -453,22 +528,24 @@ class CDRomImpl final : public PCSX::CDRom {
453528 if (debug) {
454529 auto ®s = PCSX::g_emulator->m_cpu ->m_regs ;
455530 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: %08x.%08x] w2.%i: %02x\n " , regs.pc , regs.cycle ,
456- m_registerIndex , value);
531+ m_registerAddress , value);
457532 }
458- switch (m_registerIndex ) {
533+ switch (m_registerAddress ) {
459534 case 0 : {
535+ // PARAMETER
460536 m_commandFifo.pushPayloadData (value);
461537 } break ;
462538 case 1 : {
463- m_causeMask = value;
539+ // HINT MSK (host interrupt mask)
540+ m_interruptCauseMask = value;
464541 } break ;
465542 case 2 : {
466- // Volume setting LL
467- PCSX::g_system-> log (PCSX::LogClass::CDROM, " CD-Rom: w2:2 not available yet \n " ) ;
543+ // ATV0 Left-to-Left
544+ m_atv[ 0 ] = value ;
468545 } break ;
469546 case 3 : {
470- // Volume setting RL
471- PCSX::g_system-> log (PCSX::LogClass::CDROM, " CD-Rom: w2:3 not available yet \n " ) ;
547+ // ATV3 Right-to-Left
548+ m_atv[ 3 ] = value ;
472549 } break ;
473550 }
474551 }
@@ -479,26 +556,66 @@ class CDRomImpl final : public PCSX::CDRom {
479556 if (debug) {
480557 auto ®s = PCSX::g_emulator->m_cpu ->m_regs ;
481558 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: %08x.%08x] w3.%i: %02x\n " , regs.pc , regs.cycle ,
482- m_registerIndex , value);
559+ m_registerAddress , value);
483560 }
484- switch (m_registerIndex ) {
561+ switch (m_registerAddress ) {
485562 case 0 : {
486- // ??
487- if (value == 0 ) {
563+ // clang-format off
564+ /*
565+ HCHPCTL (host chip control) register
566+ bit 7: BFRD (buffer read)
567+ The transfer of (drive) data from the buffer memory to the host is started by setting this bit high.
568+ The bit is automatically set low upon completion of the transfer.
569+ bit 6: BFWR (buffer write)
570+ The transfer of data from the host to the buffer memory is started by setting this bit high. The bit is
571+ automatically set low upon completion of the transfer.
572+ bit 5: SMEN (sound map En)
573+ This is set high to perform sound map ADPCM playback.
574+ */
575+ // clang-format on
576+ bool bfrd = value & 0x80 ;
577+ bool bfwr = value & 0x40 ;
578+ bool smen = value & 0x20 ;
579+ if (bfrd) {
580+ m_dataRequested = true ;
581+ m_dataFIFOSize = m_dataFIFOPending;
582+ } else {
488583 m_dataRequested = false ;
489584 m_dataFIFOSize = 0 ;
490585 m_dataFIFOIndex = 0 ;
491- return ;
492586 }
493- if (value == 0x80 ) {
494- m_dataRequested = true ;
495- m_dataFIFOSize = m_dataFIFOPending;
496- return ;
497- }
498- PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: w3:0(%02x) not available yet\n " , value);
499- PCSX::g_system->pause ();
587+ m_soundMapEnabled = smen;
500588 } break ;
501589 case 1 : {
590+ // clang-format off
591+ /*
592+ HCLRCTL (host clear control)
593+ When each bit of this register is set high, the chip, status, register, interrupt status and interrupt request to
594+ the host generated by the status are cleared.
595+ bit 7: CHPRST (chip reset)
596+ The inside of the IC is initialized by setting this bit high. The bit is automatically set low upon
597+ completion of the initialization of the IC. There is therefore no need for the host to reset low. When
598+ the inside of the IC is initialized by setting bit high, the XHRS pin is set low.
599+ bit 6: CLRPRM (clear parameter)
600+ The parameter register is cleared by setting this bit high. The bit is automatically set low upon
601+ completion of the clearing for the parameter register. There is therefore no need for the host to
602+ reset low.
603+ bit 5: SMADPCLR (sound map ADPCM clear)
604+ This bit is set high to terminate sound map ADPCM decoding forcibly.
605+ (1) When this bit has been set high for sound map ADPCM playback (when both SMEN and
606+ ADPBSY (HSTS register bit 2) are high):
607+ • ADPCM decoding during playback is suspended. (Noise may be generated).
608+ • The sound map and buffer management circuits in the IC are cleared, making the buffer
609+ empty. The BFEMPT interrupt status is established.
610+ (Note) Set the SMEN bit low at the same time as this bit is set high.
611+ (2) Setting this bit high when the sound map ADPCM playback is not being performed has no
612+ effect whatsoever
613+ bit 4: CLRBFWRDY (clear buffer write ready interrupt)
614+ bit 3: CLRBFEMPT (clear buffer write empty interrupt)
615+ bits 2 to 0: CLRINT#2 to 0 (clear interrupt #2 to 0)
616+ bit 4 clears the corresponding interrupt status.
617+ */
618+ // clang-format on
502619 bool ack = false ;
503620 // cause ack
504621 if (value == 0x07 ) {
@@ -534,11 +651,23 @@ class CDRomImpl final : public PCSX::CDRom {
534651 PCSX::g_system->pause ();
535652 } break ;
536653 case 2 : {
537- // Volume setting LR
538- PCSX::g_system-> log (PCSX::LogClass::CDROM, " CD-Rom: w3:2 not available yet \n " ) ;
654+ // ATV1 Left-to-Right
655+ m_atv[ 1 ] = value ;
539656 } break ;
540657 case 3 : {
541- // SPU settings latch
658+ // clang-format off
659+ /*
660+ ADPCTL (ADPCM control) register
661+ bit 5: CHNGATV (change ATV register)
662+ The host sets this bit high after the changes of the ATV 3 to 0 registers have been completed. The
663+ attenuator value in the IC is switched for the first time. There is no need for the host to set this bit
664+ low. The bit used to set the ATV3 to 0 registers of the host and to synchronize the IC audio
665+ playback.
666+ bit 0: ADPMUTE (ADPCM mute)
667+ Set high to mute the ADPCM sound for ADPCM decoding.
668+ bits 7, 6, 4 to 1: Reserved
669+ */
670+ // clang-format on
542671 PCSX::g_system->log (PCSX::LogClass::CDROM, " CD-Rom: w3:3 not available yet\n " );
543672 } break ;
544673 }
@@ -742,7 +871,7 @@ class CDRomImpl final : public PCSX::CDRom {
742871 m_invalidLocL = false ;
743872 m_speed = Speed::Simple;
744873 m_status = Status::Idle;
745- m_causeMask = 0x1f ;
874+ m_interruptCauseMask = 0x1f ;
746875 m_readingState = ReadingState::None;
747876 memset (m_lastLocP, 0 , sizeof (m_lastLocP));
748877 // Probably need to cancel other scheduled tasks here.
0 commit comments