Skip to content

Commit

Permalink
PCE: More CD-ROM emulation improvements
Browse files Browse the repository at this point in the history
-ADPCM DMA speed now matches what krikzz's test rom expects
-Implemented some delays in CD-ROM interface that mostly match the test rom
-Implemented delay when software fails to read an entire sectore before the next one is ready (improves audio sync in Sherlock Holmes movies)
-Re-fixed issues with Ys IV that were apparently caused by the read command not properly updating the SCSI signals after the drive starts seeking to the requested sector
-Fixed some ADPCM playback flags/timing issues based on okitest test rom (playing/busy flag gets turned off slightly delayed vs end reached flag, etc.)
-Implemented delay for $1805 writes, based on test rom
  • Loading branch information
SourMesen committed Dec 29, 2023
1 parent b3e414f commit 4696c2c
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 69 deletions.
72 changes: 43 additions & 29 deletions Core/PCE/CdRom/PceAdpcm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ void PceAdpcm::Reset()
_state.ReadAddress = 0;
_state.WriteAddress = 0;
_state.AddressPort = 0;
_state.Playing = false;
SetEndReached(false);
SetHalfReached(false);
_state.AdpcmLength = 0;
Expand Down Expand Up @@ -86,17 +85,10 @@ void PceAdpcm::ProcessFlags()
}

if(_state.Control & 0x80) {
LogDebug("[ADPCM] Reset");
Reset();
}

if(!(_state.Control & 0x20)) {
_state.Playing = false;
LogDebugIf(_state.Playing, "[ADPCM] Stop");
} else if(!_state.Playing) {
LogDebug("[ADPCM] Play");
_state.Playing = true;
_currentOutput = 2048;
if(_state.Control & 0x20) {
_needExec = true;
}
}
Expand All @@ -115,7 +107,6 @@ void PceAdpcm::SetControl(uint8_t value)

_state.Control = value;
ProcessFlags();

}

void PceAdpcm::ProcessReadOperation()
Expand All @@ -126,7 +117,7 @@ void PceAdpcm::ProcessReadOperation()
_emu->ProcessMemoryAccess<CpuType::Pce, MemoryType::PceAdpcmRam, MemoryOperationType::Read>(_state.ReadAddress, _state.ReadBuffer);
_state.ReadAddress++;

if(!IsLengthLatchEnabled() && !_state.EndReached) {
if(!IsLengthLatchEnabled()) {
if(_state.AdpcmLength > 0) {
_state.AdpcmLength--;
SetHalfReached(_state.AdpcmLength < 0x8000);
Expand Down Expand Up @@ -159,18 +150,27 @@ void PceAdpcm::ProcessWriteOperation()

void PceAdpcm::ProcessDmaRequest()
{
if(!_scsi->CheckSignal(Ack) && !_scsi->CheckSignal(Cd) && _scsi->CheckSignal(Io) && _scsi->CheckSignal(Req)) {
_needExec = true;
_state.WriteClockCounter = GetClocksToNextSlot(false);
_state.WriteBuffer = _scsi->GetDataPort();
_scsi->SetAckWithAutoClear();
}

if(!_scsi->IsDataBlockReady()) {
//Reset bit 0 only
//Resetting bit 1 breaks Record of Lodoss War (freezes when start is pressed)
//Not resetting bit 0 breaks Seiya Monogatari - Anearth (freezes on logo)
_state.DmaControl &= ~0x01;
if(_dmaWriteCounter) {
if(--_dmaWriteCounter == 0) {
if(!_state.WriteClockCounter) {
_state.WriteClockCounter = GetClocksToNextSlot(false);
_state.WriteBuffer = _scsi->GetDataPort();
_scsi->SetAckWithAutoClear();

if(!_scsi->IsDataBlockReady()) {
//Reset bit 0 only
//Resetting bit 1 breaks Record of Lodoss War (freezes when start is pressed)
//Not resetting bit 0 breaks Seiya Monogatari - Anearth (freezes on logo)
_state.DmaControl &= ~0x01;
}
} else {
_dmaWriteCounter++;
}
}
} else if(!_scsi->CheckSignal(Ack) && !_scsi->CheckSignal(Cd) && _scsi->CheckSignal(Io) && _scsi->CheckSignal(Req)) {
//Some delay is required here according to test rom
//Valid range is 10-14 (higher or lower fails the test)
_dmaWriteCounter = 12;
}
}

Expand All @@ -195,7 +195,7 @@ void PceAdpcm::Exec()
//Called every 3 master clocks
ProcessFlags();

if(_state.Playing) {
if(_state.Playing || (_state.Control & 0x20)) {
_nextSampleCounter += 3;
if(_nextSampleCounter >= _clocksPerSample) {
PlaySample();
Expand All @@ -212,13 +212,13 @@ void PceAdpcm::Exec()
}

bool dmaRequested = (_state.DmaControl & 0x03) != 0;
if(dmaRequested && !_state.WriteClockCounter) {
if(dmaRequested) {
ProcessDmaRequest();
}

ProcessFlags();

_needExec = _state.Playing || _state.ReadClockCounter > 0 || _state.WriteClockCounter > 0 || dmaRequested;
_needExec = _state.Playing || (_state.Control & 0x20) || _state.ReadClockCounter || _state.WriteClockCounter || dmaRequested || _dmaWriteCounter;
}

void PceAdpcm::Write(uint16_t addr, uint8_t value)
Expand Down Expand Up @@ -291,15 +291,30 @@ void PceAdpcm::PlaySample()
{
if(_state.Control & 0x80) {
//Reset flag is enabled
_state.Playing = (_state.Control & 0x20) != 0;
return;
}

if((_state.Control & 0x40) && _state.AdpcmLength == 0) {
_state.Control &= ~0x20;
}

if(!(_state.Control & 0x20)) {
_state.Playing = false;
return;
}

if(!_state.Playing) {
_state.Playing = true;
_currentOutput = 2048;
}

uint8_t data;
if(_state.Nibble) {
_state.Nibble = false;
data = _ram[_state.ReadAddress] & 0x0F;
_state.ReadAddress++;
_state.AdpcmLength--;
_state.AdpcmLength = (_state.AdpcmLength - 1) & 0x1FFFF;
} else {
_state.Nibble = true;
data = (_ram[_state.ReadAddress] >> 4) & 0x0F;
Expand All @@ -315,8 +330,6 @@ void PceAdpcm::PlaySample()

SetHalfReached(_state.AdpcmLength <= 0x8000);
if(_state.AdpcmLength == 0) {
_state.Playing = false;
_state.Control &= ~0x20; //disable play flag in control register
SetEndReached(true);
}

Expand Down Expand Up @@ -359,6 +372,7 @@ void PceAdpcm::Serialize(Serializer& s)
SV(_magnitude);
SV(_clocksPerSample);
SV(_nextSampleCounter);
SV(_dmaWriteCounter);

if(!s.IsSaving()) {
_needExec = true;
Expand Down
3 changes: 2 additions & 1 deletion Core/PCE/CdRom/PceAdpcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class PceAdpcm final : public IAudioProvider, public ISerializable
vector<int16_t> _samplesToPlay;
int16_t _currentOutput = 0;
uint8_t _magnitude = 0;
uint8_t _dmaWriteCounter = 0;
bool _needExec = true;

double _clocksPerSample = PceConstants::MasterClockRate / 32000.0;
double _nextSampleCounter = 0;

Expand Down
12 changes: 10 additions & 2 deletions Core/PCE/CdRom/PceCdRom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,14 @@ void PceCdRom::Write(uint16_t addr, uint8_t value)
}

case 0x05:
_state.ReadRightChannel = !_state.ReadRightChannel;
_state.AudioSampleLatch = _state.ReadRightChannel ? _audioPlayer.GetRightSample() : _audioPlayer.GetLeftSample();
if(_console->GetMasterClock() >= _latchChannelStamp) {
//Prevent multiple calls in a row - the delay is required to pass test rom
//The value of the L/R flag changes immediately, and writing to the register
//again within a short timeframe appears to be ignored.
_state.ReadRightChannel = !_state.ReadRightChannel;
_state.AudioSampleLatch = _state.ReadRightChannel ? _audioPlayer.GetRightSample() : _audioPlayer.GetLeftSample();
_latchChannelStamp = _console->GetMasterClock() + 700; //less than a 700 clock delay causes the test to fail
}
break;

case 0x06: break; //readonly
Expand Down Expand Up @@ -260,6 +266,8 @@ void PceCdRom::Serialize(Serializer& s)
SVArray(_saveRam, _saveRamSize);
SVArray(_cdromRam, _cdromRamSize);

SV(_latchChannelStamp);

SV(_scsi);
SV(_adpcm);
SV(_audioPlayer);
Expand Down
3 changes: 2 additions & 1 deletion Core/PCE/CdRom/PceCdRom.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class PceCdRom final : public ISerializable
PceAudioFader _audioFader;
PceCdAudioPlayer _audioPlayer;
PceCdRomState _state = {};

uint64_t _latchChannelStamp = 0;

uint8_t* _cdromRam = nullptr;
uint32_t _cdromRamSize = 0;
uint8_t* _saveRam = nullptr;
Expand Down
7 changes: 3 additions & 4 deletions Core/PCE/CdRom/PceCdSeekDelay.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,12 @@ class PceCdSeekDelay
trackGap += (1606.48 * (src - dst - 1));
}

constexpr double shortSeekFactor = 1.45; //A 1.45 factor allows both Ys IV and F1 Team Simulation to work properly
if(lbaGap < 2) {
return shortSeekFactor * (3 * 1000 / 60);
return 3 * 1000 / 60;
} else if(lbaGap < 5) {
return shortSeekFactor * ((9 * 1000 / 60) + (_sectorList[dst].RotationMs / 2));
return (9 * 1000 / 60) + (_sectorList[dst].RotationMs / 2);
} else if(trackGap <= 80) {
return shortSeekFactor * ((16 * 1000 / 60) + (_sectorList[dst].RotationMs / 2));
return (16 * 1000 / 60) + (_sectorList[dst].RotationMs / 2);
} else if(trackGap <= 160) {
return (22 * 1000 / 60) + (_sectorList[dst].RotationMs / 2);
} else if(trackGap <= 644) {
Expand Down
Loading

0 comments on commit 4696c2c

Please sign in to comment.