Mesen2/Core/PCE/CdRom/PceScsiBus.h
Sour 64126ace83 PCE: CD-ROM timing improvements
-IRQ/SCSI status should happen after seek is done when a play command is sent (fixes  audio sync in Brandish intro cutscene)
-Fixed seek delay not being applied correctly when swapping from audio playback to data loading and tweaked seek delays to make them a bit slower (fixes Brandish audio glitch during introduction)
-Fixed regression that causes the "transfer ready" irq flag to not be reset when a sector was done reading (this broke the intro sequence in "It came from the desert")
-Improved behavior when a load operation is cancelled (to match the expected results of both scsitest and verificator tests)
2024-02-03 12:00:45 +09:00

145 lines
3 KiB
C++

#pragma once
#include "pch.h"
#include "PCE/PceTypes.h"
#include "Utilities/ISerializable.h"
class Emulator;
class PceConsole;
class PceCdRom;
struct DiscInfo;
namespace ScsiSignal
{
enum ScsiSignal
{
Ack,
Atn, //Unused
Bsy, //Set when an operation is in progress
Cd, //Set when the current transfer contains commands/messages/status (cleared when actual data)
Io, //Set when drive is sending data to the software (cleared when receiving)
Msg, //Set when a message is being sent/received
Req, //Set when drive is waiting for the software to reply
Rst,
Sel,
};
}
enum class ScsiStatus
{
Good = 0x00,
};
enum class ScsiCommand
{
TestUnitReady = 0x00,
RequestSense = 0x03,
Read = 0x08,
AudioStartPos = 0xD8,
AudioEndPos = 0xD9,
Pause = 0xDA,
ReadSubCodeQ = 0xDD,
ReadToc = 0xDE
};
enum class ScsiUpdateType
{
SetCmdPhase,
SetReqSignal,
ClearReqSignal,
SetDataInPhase,
SetTransferDoneIrq,
SetGoodStatus
};
class PceScsiBus : public ISerializable
{
private:
constexpr static int ReadBytesPerSecond = 153600; //75 frames/sectors of 2048 bytes per second, like audio
Emulator* _emu = nullptr;
DiscInfo* _disc = nullptr;
PceConsole* _console = nullptr;
PceCdRom* _cdrom = nullptr;
PceScsiBusState _state = {};
bool _stateChanged = false;
int64_t _readSectorCounter = 0;
int32_t _ackClearCounter = 0;
int32_t _updateCounter = 0;
ScsiUpdateType _updateType = {};
bool _needExec = true;
vector<uint8_t> _cmdBuffer;
deque<uint8_t> _dataBuffer;
void SetSignals() {}
void ClearSignals() {}
template<typename T, typename... T2>
void SetSignals(T signal, T2... signals)
{
SetSignalValue(signal, true);
SetSignals(signals...);
}
template<typename T, typename... T2>
void ClearSignals(T signal, T2... signals)
{
SetSignalValue(signal, false);
ClearSignals(signals...);
}
void SetPhase(ScsiPhase phase);
void Reset();
void ProcessStatusPhase();
void ProcessMessageInPhase();
void QueueDriveUpdate(ScsiUpdateType action, uint32_t delay);
void ProcessDataInPhase();
uint8_t GetCommandSize(ScsiCommand cmd);
void ExecCommand(ScsiCommand cmd);
void LogCommand(string msg);
void ProcessCommandPhase();
int64_t GetSeekTime(uint32_t startLba, uint32_t targetLba);
uint64_t GetSectorLoadTime();
void CmdRead();
uint32_t GetAudioLbaPos();
void CmdAudioStartPos();
void CmdAudioEndPos();
void CmdPause();
void CmdTestReadyUnit();
void CmdReadSubCodeQ();
void CmdReadToc();
void ProcessDiscRead();
public:
PceScsiBus(Emulator* emu, PceConsole* console, PceCdRom* cdRom, DiscInfo& disc);
void SetStatusMessage(ScsiStatus status, uint8_t data, uint32_t length = 1);
PceScsiBusState& GetState() { return _state; }
uint8_t GetStatus();
bool IsDataBlockReady() { return _dataBuffer.size() > 0; }
void SetDataPort(uint8_t data);
uint8_t GetDataPort();
bool CheckSignal(::ScsiSignal::ScsiSignal signal);
void SetSignalValue(::ScsiSignal::ScsiSignal signal, bool val);
void SetAckWithAutoClear();
void UpdateState();
__noinline void Exec();
bool NeedExec() { return _needExec; }
void Serialize(Serializer& s) override;
};