[CDROM] Reads and other stuff

This commit is contained in:
liuk7071 2024-01-04 02:24:07 +01:00
parent 76f8db38a1
commit a95f467e11
11 changed files with 266 additions and 21 deletions

View file

@ -3,7 +3,10 @@
MAKE_LOG_FUNCTION(log, cdromLogger)
CDROM::CDROM(Scheduler* scheduler) : scheduler(scheduler) {
CDROM::CDROM(const fs::path& cdPath, Scheduler* scheduler) : scheduler(scheduler) {
std::string pathStr = cdPath.string();
cd = std::fopen(pathStr.c_str(), "rb");
statusReg.prmempt = 1; // Parameter fifo empty
statusReg.prmwrdy = 1; // Parameter fifo not full
@ -34,6 +37,68 @@ void CDROM::executeCommand(u8 data) {
log("SetLoc (loc: %d)\n", seekLoc);
break;
}
case CDROMCommands::ReadN: {
response.push(statusCode.raw);
scheduler->push(&int3, scheduler->time + int3Delay, this);
log("ReadN\n");
beginReading();
scheduler->push(&cdRead, scheduler->time + (!mode.doubleSpeed ? readTime : readTimeDoubleSpeed), this, "cdRead");
break;
}
case CDROMCommands::Pause: {
response.push(statusCode.raw);
scheduler->push(&int3, scheduler->time + int3Delay, this);
scheduler->push(&int2, scheduler->time + int3Delay + int2Delay, this);
stopReading();
secondResponse.push(statusCode.raw);
log("Pause\n");
break;
}
case CDROMCommands::Init: {
response.push(statusCode.raw);
secondResponse.push(statusCode.raw);
scheduler->push(&int3, scheduler->time + int3Delay, this);
scheduler->push(&int2, scheduler->time + int3Delay + int2Delay, this);
log("Init\n");
break;
}
case CDROMCommands::Setmode: {
mode.raw = getParamByte();
Helpers::debugAssert(!mode.autoPause, "Enabled CDROM auto pause\n");
Helpers::debugAssert(!mode.report, "Enabled CDROM report\n");
Helpers::debugAssert(!mode.xaFilter, "Enabled CDROM xa-filter\n");
Helpers::debugAssert(!mode.ignoreBit, "Enabled CDROM ignore bit\n");
Helpers::debugAssert(!mode.sectorSize, "Enabled CDROM 0x924 byte sectors\n");
Helpers::debugAssert(!mode.xaAdpcm, "Enabled CDROM xa-adpcm\n");
response.push(statusCode.raw);
scheduler->push(&int3, scheduler->time + int3Delay, this);
log("Setmode\n");
break;
}
case CDROMCommands::Demute: {
response.push(statusCode.raw);
secondResponse.push(statusCode.raw);
scheduler->push(&int3, scheduler->time + int3Delay, this);
log("Demute\n");
break;
}
case CDROMCommands::SeekL: {
// TODO: SeekL/P should stop ongoing reads
@ -171,6 +236,51 @@ void CDROM::stopSeeking(void* classptr) {
log("Seek ended\n");
}
void CDROM::beginReading() {
statusCode.reading = true;
log("Begin reading\n");
}
void CDROM::stopReading() {
statusCode.reading = false;
log("Stop reading\n");
scheduler->deleteAllEventsOfName("cdRead");
}
void CDROM::cdRead(void* classptr) {
CDROM* cdrom = (CDROM*)classptr;
Helpers::debugAssert((cdrom->intFlag & 7) == 0, "[ FATAL ] CDROM INT1 was fired before previous INT%d was acknowledged in interrupt flag\n", cdrom->intFlag & 3);
cdrom->intFlag |= 1;
cdrom->sector.clear();
cdrom->sector.resize(sectorSize);
std::fseek(cdrom->cd, (cdrom->seekLoc - 150) * sectorSize, SEEK_SET);
std::fread(cdrom->sector.data(), 1, sectorSize, cdrom->cd);
cdrom->statusReg.drqsts = 1;
log("Read sector %d\n", cdrom->seekLoc);
cdrom->seekLoc++;
if (cdrom->mode.sectorSize)
cdrom->sectorCur = 0x0C;
else
cdrom->sectorCur = 0x18;
cdrom->scheduler->push(&cdrom->cdRead, cdrom->scheduler->time + (!cdrom->mode.doubleSpeed ? readTime : readTimeDoubleSpeed), classptr, "cdRead");
}
u32 CDROM::readSectorWord() {
if ((sectorCur + 3) >= sector.size()) Helpers::panic("[ FATAL ] CDROM sector read out of bounds\n");
u32 word;
std::memcpy(&word, &sector[sectorCur], sizeof(u32));
sectorCur += sizeof(u32);
if (sectorCur == sectorSize) statusReg.drqsts = 0;
return word;
}
void CDROM::pushParam(u8 data) {
params.push(data);
Helpers::debugAssert(params.size() <= 16, "[ FATAL ] Wrote more than 16 bytes to CDROM parameter fifo");
@ -187,6 +297,10 @@ void CDROM::writeStatus(u8 data) {
statusReg.index = data & 3;
}
u8 CDROM::readIE() {
return intEnable;
}
void CDROM::writeIE(u8 data) {
intEnable = data;
}

View file

@ -8,21 +8,25 @@
constexpr u64 cpuSpeed = 33868800;
constexpr u64 readDelay = cpuSpeed / 75;
constexpr u64 readDelayDoubleSpeed = readDelay / 2;
constexpr u64 readTime = cpuSpeed / 75;
constexpr u64 readTimeDoubleSpeed = readTime / 2;
// I don't know if these are ok?????
constexpr u64 int3Delay = cpuSpeed / 1000;
constexpr u64 int3Delay = cpuSpeed / 15000;
constexpr u64 int2Delay = int3Delay * 2;
constexpr u64 getIDDelay = 33868;
constexpr u64 seekTime = 150000; // Currently stubbed seeking time to this for all seeks
constexpr u64 seekTime = 75000; // Currently stubbed seeking time to this for all seeks
constexpr u64 sectorSize = 0x930;
class CDROM {
public:
CDROM(Scheduler* scheduler);
CDROM(const fs::path& cdPath, Scheduler* scheduler);
Scheduler* scheduler;
FILE* cd;
void executeCommand(u8 data);
// INTs
@ -34,11 +38,16 @@ public:
static void stopSeeking(void* classptr);
void beginReading();
void stopReading();
static void cdRead(void* classptr);
void pushParam(u8 data);
u8 readStatus();
void writeStatus(u8 data);
u8 readIE();
void writeIE(u8 data);
u8 readIF();
void writeIF(u8 data);
@ -46,7 +55,11 @@ public:
u8 getIndex();
u8 getResponseByte(); // This one is public so it can be called from the memory read handlers
u32 readSectorWord();
private:
std::vector<u8> sector;
u64 sectorCur = 0;
u8 intEnable = 0;
u8 intFlag = 0;
@ -56,6 +69,18 @@ private:
u32 seekLoc = 0;
u8 bcdToDec(u8 n) { return n - 6 * (n >> 4); }
union {
u8 raw = 0;
BitField<0, 1, u8> cdda;
BitField<1, 1, u8> autoPause;
BitField<2, 1, u8> report;
BitField<3, 1, u8> xaFilter;
BitField<4, 1, u8> ignoreBit;
BitField<5, 1, u8> sectorSize;
BitField<6, 1, u8> xaAdpcm;
BitField<7, 1, u8> doubleSpeed;
} mode;
std::queue<u8> response;
std::queue<u8> secondResponse;
@ -87,6 +112,11 @@ namespace CDROMCommands {
enum {
GetStat = 0x01,
SetLoc = 0x02,
ReadN = 0x06,
Pause = 0x09,
Init = 0x0A,
Setmode = 0x0E,
Demute = 0x0C,
SeekL = 0x15,
Test = 0x19,
GetID = 0x1A

View file

@ -6,6 +6,7 @@ MAKE_LOG_FUNCTION(log, dmaLogger)
DMA::DMA() {
channels[2].doDMA = &gpuDMA;
channels[3].doDMA = &cdromDMA;
channels[6].doDMA = &otcDMA;
}
@ -98,6 +99,35 @@ void DMA::gpuDMA(Memory* memory) {
log("GPU DMA done\n");
}
void DMA::cdromDMA(Memory* memory) {
const auto& dma = memory->dma;
auto& ch = dma->channels[3];
log("Start CDROM DMA\n");
switch (ch.chcr.syncMode) {
case (u32)SyncMode::Block: {
u32 addr = ch.madr & 0x1ffffc;
u32 bc = ch.bcr.bc;
if (!bc) bc = 0x10000;
while (bc-- > 1) {
memory->write<u32>(addr, memory->cdrom->readSectorWord());
if (ch.chcr.step == (u32)Step::Forward)
addr += 4;
else
addr -= 4;
}
break;
}
default:
Helpers::panic("[DMA] Unimplemented CDROM DMA sync mode %d\n", ch.chcr.syncMode.Value());
}
ch.chcr.enable = 0;
ch.chcr.trigger = 0;
// TODO: Update DICR
log("CDROM DMA done\n");
}
void DMA::otcDMA(Memory* memory) {
const auto& dma = memory->dma;
auto& ch = dma->channels[6];

View file

@ -35,7 +35,7 @@ public:
} chcr;
bool shouldStartDMA();
void (*doDMA)(Memory*);
void (*doDMA)(Memory*) = nullptr;
};
DMAChannel channels[7];
@ -59,5 +59,6 @@ public:
void doDMA(int channel, Memory* memory);
static void gpuDMA(Memory* memory);
static void cdromDMA(Memory* memory);
static void otcDMA(Memory* memory);
};

View file

@ -10,10 +10,10 @@ int main(int argc, char** argv) {
printf("ChonkyStation\n");
PlayStation playstation = PlayStation(argv[1]);
PlayStation playstation = PlayStation(argv[1], argv[2]);
if (argc >= 3) {
playstation.sideloadExecutable(argv[2]);
if (argc >= 4) {
playstation.sideloadExecutable(argv[3]);
}
// Testing

View file

@ -76,6 +76,7 @@ u8 Memory::read(u32 vaddr) {
}
else if (paddr == 0x1f801803) {
switch (cdrom->getIndex()) {
case 0: return cdrom->readIE();
case 1: return cdrom->readIF();
default:
Helpers::panic("[ FATAL ] Unhandled CDROM read8 0x1f801803.%d", cdrom->getIndex());
@ -113,6 +114,8 @@ u16 Memory::read(u32 vaddr) {
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::SIO, (u32)MemorySize::SIO)) return 0;
// SPU
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::SPU, (u32)MemorySize::SPU)) return 0;
// Timers
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::Timer, (u32)MemorySize::Timer)) return 0;
else
Helpers::panic("[ FATAL ] Unhandled read16 0x%08x (virtual 0x%08x)\n", paddr, vaddr);
}
@ -158,7 +161,10 @@ u32 Memory::read(u32 vaddr) {
// Timers
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::Timer, (u32)MemorySize::Timer)) return 0;
else if (paddr == 0x1f802080) return 0; // 1F802080h 4 Redux-Expansion ID "PCSX" (R)
else if (paddr == 0x1f80101C) return 0x00070777; // Expansion 2 Delay/Size (usually 00070777h) (128 bytes, 8bit bus)
else if (paddr == 0x1f801060) return 0x00000b88; // RAM_SIZE (R/W) (usually 00000B88h) (or 00000888h)
else
Helpers::dump("ramdump.bin", ram, 2_MB);
@ -188,6 +194,9 @@ void Memory::write(u32 vaddr, u8 data) {
cdrom->executeCommand(data);
break;
}
case 1: break;
case 2: break;
case 3: break;
default:
Helpers::panic("[ FATAL ] Unhandled CDROM write8 0x1f801801.%d <- 0x%02x\n", cdrom->getIndex(), data);
}
@ -202,20 +211,35 @@ void Memory::write(u32 vaddr, u8 data) {
cdrom->writeIE(data);
break;
}
case 2: break;
case 3: break;
default:
Helpers::panic("[ FATAL ] Unhandled CDROM write8 0x1f801802.%d <- 0x%02x\n", cdrom->getIndex(), data);
}
}
else if (paddr == 0x1f801803) {
switch (cdrom->getIndex()) {
case 0: {
Helpers::debugAssert(((data >> 5) & 1) == 0, "[ FATAL ] Unhandled CDROM Request Register SMEN bit\n");
break;
}
case 1: {
cdrom->writeIF(data);
break;
}
case 2: break;
case 3: break;
default:
Helpers::panic("[ FATAL ] Unhandled CDROM write8 0x1f801803.%d <- 0x%02x\n", cdrom->getIndex(), data);
}
}
// 1F802080h 1 Redux-Expansion Console putchar (W)
else if (paddr == 0x1f802080) {
std::putc(data, stdout);
return;
}
else if (paddr == 0x1f802041) return; // POST - External 7-segment Display (W)
else
Helpers::panic("[ FATAL ] Unhandled write8 0x%08x (virtual 0x%08x) <- 0x%02x\n", paddr, vaddr, data);

View file

@ -12,11 +12,11 @@
class PlayStation {
public:
PlayStation(const fs::path& biosPath) : cdrom(&scheduler), interrupt(), gpu(&scheduler), dma(), mem(&interrupt, &dma, &gpu, &cdrom), cpu(&mem) {
PlayStation(const fs::path& biosPath, const fs::path& cdPath) : cdrom(cdPath, &scheduler), interrupt(), gpu(&scheduler), dma(), mem(&interrupt, &dma, &gpu, &cdrom), cpu(&mem) {
mem.core = &cpu.core;
mem.loadBios(biosPath);
cpu.switchBackend(Cpu::Backend::OldInterpreter);
cpu.switchBackend(Cpu::Backend::Interpreter);
// Setup GPU scheduler events
scheduler.push(&gpu.scanlineEvent, scheduler.time + GPUConstants::cyclesPerScanline, &gpu);

View file

@ -13,8 +13,18 @@ void Scheduler::tick(u64 cycles) {
}
}
void Scheduler::push(void (*functionPtr)(void*), u64 time, void* data) {
void Scheduler::push(void (*functionPtr)(void*), u64 time, void* data, std::string name) {
Helpers::debugAssert(events.size() < schedulerMaxEntries, "[ FATAL ] Queued more than %d scheduler events\n", schedulerMaxEntries);
events.push({ .functionPtr = functionPtr, .data = data, .time = time });
events.push({ .functionPtr = functionPtr, .data = data, .time = time, .name = name });
}
void Scheduler::deleteAllEventsOfName(std::string name) {
auto eventList = events.get_container();
for (int i = 0; i < events.size(); i++) {
for (auto& i : eventList) {
if (i.name == name) events.remove(i);
}
}
}

View file

@ -2,25 +2,61 @@
#include <helpers.hpp>
#include <queue>
#include <set>
constexpr auto schedulerMaxEntries = 64;
// https://stackoverflow.com/questions/19467485/how-to-remove-element-not-at-top-from-priority-queue
template<typename T>
class pqueue : public std::priority_queue<T, std::vector<T>, std::greater<T>> {
public:
std::vector<T>& get_container() {
return this->c;
}
bool remove(const T& value) {
auto it = std::find(this->c.begin(), this->c.end(), value);
if (it == this->c.end()) {
return false;
}
if (it == this->c.begin()) {
// Deque the top element
this->pop();
}
else {
// Remove element and re-heap
this->c.erase(it);
std::make_heap(this->c.begin(), this->c.end(), this->comp);
}
return true;
}
};
class Scheduler {
public:
u64 time = 0;
void tick(u64 cycles);
struct Event {
void (*functionPtr)(void*);
void* data;
u64 time;
void (*functionPtr)(void*) = nullptr;
void* data = nullptr;
u64 time = 0;
std::string name = "Default";
bool operator==(const Event& other) const {
return (functionPtr == other.functionPtr) && (data == other.data) && (time == other.time) && (name == other.name);
}
bool operator>(const Event& other) const {
return time > other.time;
}
};
std::priority_queue<Event, std::vector<Event>, std::greater<Event>> events;
void push(void (*functionPtr)(void*), u64 time, void* data);
pqueue<Event> events;
void push(void (*functionPtr)(void*), u64 time, void* data, std::string name = "Default");
void deleteAllEventsOfName(std::string name);
};

View file

@ -3,7 +3,7 @@
using namespace Test;
Diff::Diff(const fs::path& biosPath) : p1(biosPath), p2(biosPath) {
Diff::Diff(const fs::path& biosPath, const fs::path& cdPath) : p1(biosPath, cdPath), p2(biosPath, cdPath) {
p1.switchCpuBackend(Cpu::Backend::Interpreter);
p2.switchCpuBackend(Cpu::Backend::OldInterpreter);
}

View file

@ -7,7 +7,7 @@
namespace Test {
class Diff {
public:
Diff(const fs::path& biosPath);
Diff(const fs::path& biosPath, const fs::path& cdPath);
void doTest();
private:
PlayStation p1;