Skip to content

Commit

Permalink
Additional CdRom and Gte opcodes implementation
Browse files Browse the repository at this point in the history
- PlayStation logo rendering
- First GTE opcodes implementation: RTPT, NCLIP, AVSZ3 and NCDS
- COP2 opcodes implementation: MFC2, CFC2, MTC2, SWC2
- CdRom XA support: support reading of the Mode 2 Form 1 sectors, Mode 2 Form 2 and whole sector reads
- CdRom Init and Demute commands
  • Loading branch information
oaleshina committed Apr 19, 2019
1 parent 1193964 commit e7c300f
Show file tree
Hide file tree
Showing 22 changed files with 1,473 additions and 255 deletions.
109 changes: 83 additions & 26 deletions pscx_emulator/pscx_cdrom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "pscx_cpu.h"

// ***************** Fifo implementation *********************
Fifo Fifo::fromBytes(std::vector<uint8_t> bytes)
Fifo Fifo::fromBytes(const std::vector<uint8_t>& bytes)
{
Fifo fifo;
for (auto byte : bytes)
Expand All @@ -22,7 +22,7 @@ bool Fifo::isEmpty() const

bool Fifo::isFull() const
{
// The FIFO is full if both indexes point to the same cell
// The FIFO is full if both indices point to the same cell
// while having a different carry.
return m_writeIdx == (m_readIdx ^ 0x10);
}
Expand Down Expand Up @@ -64,22 +64,27 @@ T CdRom::load(TimeKeeper& timeKeeper, InterruptState& irqState, uint32_t offset)
switch (offset)
{
case 0x0:
{
value = getStatus();
break;
}
case 0x1:
if (m_response.isEmpty())
{
if(m_response.isEmpty())
LOG("CDROM response FIFO underflow");

value = m_response.pop();
break;
}
case 0x3:
{
// IRQ mask/flags have the 3 MSB set when read.
if (m_index == 0x0)
value = m_irqMask | 0xe0;
else if (m_index == 0x1)
value = m_irqFlags | 0xe0;
break;
}
}
return (uint32_t)value;
}

Expand All @@ -94,9 +99,7 @@ void CdRom::store(TimeKeeper& timeKeeper, InterruptState& irqState, uint32_t off

sync(timeKeeper, irqState);

// All writeable registers are 8 bit wide
// bool prevIrq = irq();

// All writeable registers are 8 bit wide.
switch (offset)
{
case 0x0:
Expand Down Expand Up @@ -127,12 +130,6 @@ void CdRom::store(TimeKeeper& timeKeeper, InterruptState& irqState, uint32_t off
}
break;
}

//if (!prevIrq && irq())
//{
// Interrupt rising edge
// irqState.raiseAssert(Interrupt::INTERRUPT_CDROM);
//}
}

template void CdRom::store<uint32_t>(TimeKeeper&, InterruptState&, uint32_t, uint32_t);
Expand Down Expand Up @@ -231,9 +228,9 @@ void CdRom::sync(TimeKeeper& timeKeeper, InterruptState& irqState)

uint8_t CdRom::readByte()
{
assert(m_rxIndex < 0x800, "Unhandled CDROM long read");
assert(m_rxIndex < m_rxLen, "Unhandled CDROM long read");

uint8_t byte = m_rxSector->getDataByte(m_rxIndex);
uint8_t byte = m_rxSector->getDataByte(m_rxOffset + m_rxIndex);

assert(m_rxActive, "Read byte while !m_rxActive");
m_rxIndex += 1;
Expand Down Expand Up @@ -265,6 +262,19 @@ void CdRom::sectorRead(InterruptState& irqState)
assert(resultXaSector.getSectorStatus() == XaSector::XaSectorStatus::XA_SECTOR_STATUS_OK, "Couldn't read sector");
m_rxSector = resultXaSector.getSectorPtr();

if (m_readWholeSector)
{
// Read the entire sector except for the sync pattern
m_rxOffset = 12;
m_rxLen = 2340;
}
else
{
// Read 2048 bytes after the Mode2 XA sub-header
m_rxOffset = 24;
m_rxLen = 2048;
}

if (m_irqFlags == 0x0)
{
m_response = Fifo::fromBytes({ getDriveStatus() });
Expand All @@ -285,7 +295,9 @@ uint8_t CdRom::getStatus()
status |= ((uint8_t)m_params.isEmpty()) << 3;
status |= ((uint8_t)(!m_params.isFull())) << 4;
status |= ((uint8_t)(!m_params.isEmpty())) << 5;
status |= 0 << 6;

bool dataAvailable = m_rxIndex < m_rxLen;
status |= (uint8_t)dataAvailable << 6;
// "Busy" flag
if (m_commandState == CommandState::COMMAND_STATE_RX_PENDING)
status |= 1 << 7;
Expand Down Expand Up @@ -351,7 +363,7 @@ void CdRom::setConfig(uint8_t config)
m_rxIndex = (m_rxIndex & ~7) + ((m_rxIndex & 4) << 1);
}

assert((config & 0x7f) == 0, "CDROM: unhandled config");
assert((config & 0x5f) == 0, "CDROM: unhandled config");
}

void CdRom::irqMask(uint8_t value)
Expand All @@ -362,7 +374,7 @@ void CdRom::irqMask(uint8_t value)
uint32_t CdRom::getCyclesPerSector()
{
// 1x speed: 75 sectors per second
return (CPU_FREQ_HZ / 75) >> m_doubleSpeed;
return (CPU_FREQ_HZ / 75) >> (uint32_t)m_doubleSpeed;
}

void CdRom::command(TimeKeeper& timeKeeper, uint8_t cmd)
Expand All @@ -386,6 +398,12 @@ void CdRom::command(TimeKeeper& timeKeeper, uint8_t cmd)
case 0x09:
onAcknowledge = &CdRom::cmdPause;
break;
case 0x0a:
onAcknowledge = &CdRom::cmdInit;
break;
case 0x0c:
onAcknowledge = &CdRom::cmdDemute;
break;
case 0x0e:
onAcknowledge = &CdRom::cmdSetMode;
break;
Expand Down Expand Up @@ -444,7 +462,7 @@ uint8_t CdRom::getDriveStatus() const
driveStatus |= ((uint8_t)reading) << 5;
return driveStatus;
}
// No disc, pretemd that the shell is open ( bit 4 ).
// No disc, pretend that the shell is open ( bit 4 ).
return 0x10;
}

Expand All @@ -454,8 +472,6 @@ CommandState CdRom::cmdGetStat()
// and then put a 2nd byte 0x20 to signal the wrong number of params.
assert(m_params.isEmpty(), "Unexpected parmeters for CDROM GetStat command");

// m_response.push(0x10);
// triggerIrq(IrqCode::IRQ_CODE_OK);
Fifo response;
response.push(getDriveStatus());

Expand Down Expand Up @@ -533,17 +549,41 @@ CommandState CdRom::cmdPause()
return CommandState::COMMAND_STATE_RX_PENDING;
}

CommandState CdRom::cmdInit()
{
m_onAcknowledge = &CdRom::ackInit;

m_helperRxPending.m_rxDelay = 58'000;
m_helperRxPending.m_irqDelay = 58'000 + 5401;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_OK;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
}

CommandState CdRom::cmdDemute()
{
// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 32'000;
m_helperRxPending.m_irqDelay = 32'000; //+ 5401;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_OK;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
}

CommandState CdRom::cmdSetMode()
{
assert(m_params.len() == 1, "CDROM: bad number of parameters for SetMode");

uint8_t mode = m_params.pop();

m_doubleSpeed = mode & 0x80;
m_readWholeSector = mode & 0x20;

assert((mode & 0x7f) == 0x0, "CDROM: unhandled mode");

// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 22'000;
m_helperRxPending.m_irqDelay = 22'000 + 5391;
m_helperRxPending.m_irqDelay = 22'000; //+ 5391;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_OK;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
Expand All @@ -552,7 +592,7 @@ CommandState CdRom::cmdSetMode()
CommandState CdRom::cmdSeekl()
{
// Make sure that we don't end up in the track 1's pregap.
assert(m_seekTarget > MinuteSecondFrame::fromBCD(0x0, 0x2, 0x0), "Seek to track 1 pregap");
assert(m_seekTarget >= MinuteSecondFrame::fromBCD(0x0, 0x2, 0x0), "Seek to track 1 pregap");

m_readPosition = m_seekTarget;
m_onAcknowledge = &CdRom::ackSeekl;
Expand Down Expand Up @@ -647,8 +687,9 @@ CommandState CdRom::ackSeekl()
{
// The seek itself takes a while to finish since the drive has
// to physically move the head.
// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 1'000'000;
m_helperRxPending.m_irqDelay = 1'000'000 + 1859;
m_helperRxPending.m_irqDelay = 1'000'000; //+ 1859;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_DONE;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
Expand Down Expand Up @@ -687,8 +728,9 @@ CommandState CdRom::ackGetId()
regionSymbol
});

// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 7'336;
m_helperRxPending.m_irqDelay = 7'336 + 12'376;
m_helperRxPending.m_irqDelay = 7'336; //+ 12'376;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_DONE;
m_helperRxPending.m_response = response;
return CommandState::COMMAND_STATE_RX_PENDING;
Expand Down Expand Up @@ -716,8 +758,23 @@ CommandState CdRom::ackPause()
{
m_readState = ReadState::READ_STATE_IDLE;

// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 2'000'000;
m_helperRxPending.m_irqDelay = 2'000'000; //+ 1858;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_DONE;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
}

CommandState CdRom::ackInit()
{
m_readState = ReadState::READ_STATE_IDLE;
m_doubleSpeed = false;
m_readWholeSector = true;

// Fixme: irq delay.
m_helperRxPending.m_rxDelay = 2'000'000;
m_helperRxPending.m_irqDelay = 2'000'000 + 1858;
m_helperRxPending.m_irqDelay = 2'000'000; //+ 1870;
m_helperRxPending.m_irqCode = IrqCode::IRQ_CODE_DONE;
m_helperRxPending.m_response = Fifo::fromBytes({ getDriveStatus() });
return CommandState::COMMAND_STATE_RX_PENDING;
Expand Down
24 changes: 22 additions & 2 deletions pscx_emulator/pscx_cdrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct Fifo
memset(m_buffer, 0x0, sizeof(m_buffer));
}

static Fifo fromBytes(std::vector<uint8_t> bytes);
static Fifo fromBytes(const std::vector<uint8_t>& bytes);

bool isEmpty() const;
bool isFull() const;
Expand Down Expand Up @@ -98,7 +98,10 @@ struct CdRom
m_readPosition(MinuteSecondFrame::createZeroTimestamp()),
m_doubleSpeed(false),
m_rxActive(false),
m_rxIndex(0x0)
m_rxIndex(0x0),
m_rxOffset(0x0),
m_rxLen(0x0),
m_readWholeSector(true)
{}

template<typename T>
Expand Down Expand Up @@ -149,6 +152,10 @@ struct CdRom
CommandState cmdReadN();
// Stop reading sectors, but remain at the same position on the disc.
CommandState cmdPause();
// Reinitialize the CD ROM controller.
CommandState cmdInit();
// Demute CDROM audio playback.
CommandState cmdDemute();
// Configure the behaviour of the CDROM drive
CommandState cmdSetMode();
// Execute seek. Target is given by previous SetLoc command.
Expand All @@ -170,6 +177,7 @@ struct CdRom
CommandState ackGetId();
CommandState ackReadToc();
CommandState ackPause();
CommandState ackInit();

void pushParam(uint8_t param);

Expand Down Expand Up @@ -205,6 +213,9 @@ struct CdRom

struct Reading
{
Reading() :
m_delay(0x0)
{}
uint32_t m_delay;
} m_helperReading;

Expand Down Expand Up @@ -249,4 +260,13 @@ struct CdRom

// Index of the next byte to be read in the RX sector.
uint16_t m_rxIndex;

// Offset of m_rxIndex in the sector buffer
uint16_t m_rxOffset;
// Index of the last byte to be read in the RX sector
uint16_t m_rxLen;

// If true we read the whole sector except for the sync bytes
// (0x924 bytes), otherwise it only reads 0x800 bytes.
bool m_readWholeSector;
};
4 changes: 3 additions & 1 deletion pscx_emulator/pscx_cop0.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "pscx_cop0.h"
#include <iostream>

uint32_t Cop0::getStatusRegister() const
{
Expand Down Expand Up @@ -63,8 +64,9 @@ uint32_t Cop0::enterException(Exception cause, uint32_t pc, bool inDelaySlot)

void Cop0::returnFromException()
{
//std::cout << "return from exception" << std::endl;
uint32_t mode = m_sr & 0x3f;
m_sr &= (~0x3f);
m_sr &= (~0xf);
m_sr |= (mode >> 2);
}

Expand Down
Loading

0 comments on commit e7c300f

Please sign in to comment.