mirror of
https://github.com/liuk7071/ChonkyStation.git
synced 2024-05-10 00:24:07 -04:00
[CDROM] Reads and other stuff
This commit is contained in:
parent
76f8db38a1
commit
a95f467e11
|
@ -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, §or[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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue