mirror of
https://github.com/shadergz/cosmic-station.git
synced 2024-11-27 00:00:21 +00:00
Core
: Fixes various inconsistencies and improves code efficiency
This commit is contained in:
parent
1dc6a03a69
commit
61b6374a80
@ -1,14 +1,13 @@
|
||||
# Cosmic Project
|
||||
|
||||
### Project Progress:
|
||||
- Progression: ```17%```
|
||||
- Progression: ```19%```
|
||||
- Current version: 0.0.20 (Pre-Boot stage)
|
||||
- Target for the first demo release: 2025
|
||||
- Top priority: Adding the IO coprocessor timers (IOP TIMERS)
|
||||
- Top priority: Test everything already done
|
||||
|
||||
### Special thanks
|
||||
- [DobieStation](https://github.com/PSI-Rockin/DobieStation.git) Used as the main reference for the project
|
||||
- [Strato](https://github.com/strato-emu/strato.git) The main structure of the project was based on another emulation project
|
||||
- [libadrenotools](https://github.com/bylaws/libadrenotools.git) The custom driver management system was implemented following the steps of this project
|
||||
|
||||
### Reference to third-party resources and assets used
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
namespace cosmic::ee {
|
||||
bool CtrlCop::getCondition() {
|
||||
u32 stat{mio::bitBashing<u32>(dmac->performRead(0x1000e10)) & 0x3ff};
|
||||
u32 pcr{mio::bitBashing<u32>(dmac->performRead(0x1000e020)) & 0x3ff};
|
||||
u32 stat{mio::BitBashing<u32>(dmac->performRead(0x1000e10)) & 0x3ff};
|
||||
u32 pcr{mio::BitBashing<u32>(dmac->performRead(0x1000e020)) & 0x3ff};
|
||||
return ((~pcr | stat) & 0x3ff) == 0x3ff;
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace cosmic::iop {
|
||||
"Usb",
|
||||
"EXTR",
|
||||
"FireWire",
|
||||
"FDma" // FIreWire DMA? (unknown)
|
||||
"FDma" // FireWire DMA? (unknown)
|
||||
};
|
||||
if (stat < iopIrqStats.size()) {
|
||||
user->info("Stat flag being read, output value: {}", iopIrqStats[stat]);
|
||||
|
@ -7,13 +7,37 @@ namespace cosmic::iop {
|
||||
ioSched->modifyTimerSet(intEvents[index],
|
||||
vm::TimerSet::Pause, std::vector<u64>{0});
|
||||
}
|
||||
struct IopTimersCct {
|
||||
u32 counter, control, target;
|
||||
};
|
||||
std::array<IopTimersCct, 1> iopTimersArea {
|
||||
{{0x1f801120, 0x1f801124, 0x1f801128}}
|
||||
};
|
||||
os::vec IopTimers::performTimerAccess(u32 address, u32 value, bool write) {
|
||||
u64 iopTimerIndex{};
|
||||
os::vec result{};
|
||||
ranges::for_each(iopTimersArea, [&](const auto& iotMap) {
|
||||
if (iotMap.counter >= address && iotMap.target <= address) {
|
||||
if (iotMap.counter == address && write)
|
||||
writeCounter(iopTimerIndex, value);
|
||||
else if (iotMap.control == address && write)
|
||||
writeCtrl(iopTimerIndex, static_cast<u16>(value));
|
||||
else if (iotMap.target == address && write)
|
||||
writeTarget(iopTimerIndex, value);
|
||||
}
|
||||
iopTimerIndex++;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IopTimers::IopTimers(std::shared_ptr<vm::Scheduler> &source,
|
||||
std::shared_ptr<console::IntCInfra> &infra) :
|
||||
ioSched(source), infra(infra) {
|
||||
}
|
||||
void IopTimers::resetIoTimers() {
|
||||
ranges::for_each(ioTimers, [](auto& timer){
|
||||
ranges::for_each(ioTimers,
|
||||
[](auto& timer){
|
||||
timer = {};
|
||||
});
|
||||
timerIntEnbId = ioSched->createSchedTick(
|
||||
@ -130,7 +154,7 @@ namespace cosmic::iop {
|
||||
clockRate = 0;
|
||||
}
|
||||
enum PreScalesFactor {
|
||||
Normal, // IOP clock,
|
||||
Normal, // IOP clock
|
||||
Scale8,
|
||||
Scale16,
|
||||
Scale256
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <console/intc.h>
|
||||
#include <vm/sched_logical.h>
|
||||
#include <os/neon_simd.h>
|
||||
namespace cosmic::iop {
|
||||
struct TimerControl {
|
||||
bool useGate;
|
||||
@ -12,7 +13,7 @@ namespace cosmic::iop {
|
||||
bool cmpIntEnb;
|
||||
bool overIntEnb;
|
||||
bool repeatInt;
|
||||
// toggle bit 10 (intEnable) on IRQs if bit 6 (repeatInt) is set.
|
||||
// toggle bit 10 (intEnable) on IRQs if bit 6 (repeatInt) is set
|
||||
bool toggleInt;
|
||||
bool intEnable;
|
||||
bool externSignal;
|
||||
@ -38,6 +39,8 @@ namespace cosmic::iop {
|
||||
void writeCounter(u64 index, u32 value);
|
||||
void writeCtrl(u64 index, u16 value);
|
||||
void writeTarget(u64 index, u32 value);
|
||||
|
||||
os::vec performTimerAccess(u32 address, u32 value, bool write);
|
||||
private:
|
||||
void timerIrqTest(u64 index, bool overflow);
|
||||
[[clang::always_inline]] void clearTimerCounter(u64 index);
|
||||
|
@ -24,6 +24,12 @@ namespace cosmic::mio {
|
||||
[[maybe_unused]] void iopSoftClean() {
|
||||
memset(*iopBlock, 0, iopBlock.getBlockSize());
|
||||
}
|
||||
[[maybe_unused]] void sndSoftClean() {
|
||||
memset(*sndBlock, 0, sndBlock.getBlockSize());
|
||||
}
|
||||
[[maybe_unused]] void ramSoftClean() {
|
||||
memset(*ramBlock, 0, ramBlock.getBlockSize());
|
||||
}
|
||||
void printMemoryImage();
|
||||
private:
|
||||
void dumpMemoryToDisk(boost::filesystem::path& devOutFile,
|
||||
|
@ -69,7 +69,7 @@ namespace cosmic::mio {
|
||||
}
|
||||
// At this point, we are waiting for the data in memory at the specified address
|
||||
// We cannot continue the transfer without first triggering an interrupt
|
||||
user->info("The channel {} is waiting ({} | {})", channelsName[chan.index], chan.adr, stallAddress);
|
||||
user->info("The channel {} is waiting ({} - {})", channelsName[chan.index], chan.adr, stallAddress);
|
||||
raiseInt1();
|
||||
|
||||
intStat.channelStat[DmaStall] = true;
|
||||
@ -95,7 +95,7 @@ namespace cosmic::mio {
|
||||
}
|
||||
os::vec DmaController::performRead(u32 address) {
|
||||
os::vec fetched{};
|
||||
auto dmaHas{dmaVirtSolver(address)};
|
||||
const auto dmaHas{dmaVirtSolver(address)};
|
||||
if (!dmaHas) {
|
||||
return fetched;
|
||||
}
|
||||
|
@ -6,27 +6,31 @@
|
||||
namespace cosmic::mio {
|
||||
VirtualPointer MemoryPipe::solveGlobal(u32 address, PipeAccess dev) {
|
||||
auto isMips{dev == IopDev || dev == CoreDevices};
|
||||
VirtualPointer virtAddress{};
|
||||
|
||||
if (address >= 0x1fc00000 && address < 0x20000000 && isMips) {
|
||||
return directPointer(address, dev);
|
||||
virtAddress = directPointer(address, dev);
|
||||
}
|
||||
if (dev == IopDev) {
|
||||
if (address < 0x00200000)
|
||||
return directPointer(address, dev);
|
||||
return iopHalLookup(address);
|
||||
virtAddress = directPointer(address, dev);
|
||||
else
|
||||
virtAddress = iopHalLookup(address);
|
||||
} else if (dev == CoreDevices) {
|
||||
return directPointer(address, dev);
|
||||
virtAddress = directPointer(address, dev);
|
||||
}
|
||||
return {};
|
||||
return virtAddress;
|
||||
}
|
||||
MemoryPipe::MemoryPipe(std::shared_ptr<console::VirtDevices>& devices) : devs(devices) {
|
||||
MemoryPipe::MemoryPipe(std::shared_ptr<console::VirtDevices>& devices) :
|
||||
devs(devices) {
|
||||
}
|
||||
struct GlobalRangeSpecs {
|
||||
u32 starts;
|
||||
u32 ends;
|
||||
MemoryPipe::MemoryOrderFuncId funcId;
|
||||
MemoryPipe::MemoryOrderFuncId function;
|
||||
};
|
||||
|
||||
os::vec MemoryPipe::imageDecoderGlb(u32 address, os::vec value, u64 size, bool ro) {
|
||||
os::vec MemoryPipe::imageDecoderGlb(u32 address, const os::vec& value, u64 size, bool ro) {
|
||||
os::vec ipu{};
|
||||
|
||||
switch (address) {
|
||||
@ -34,77 +38,87 @@ namespace cosmic::mio {
|
||||
if (size != sizeof(u32))
|
||||
break;
|
||||
if (ro)
|
||||
devs->decoderMpeg12->issueACmd(bitBashing<u32>(value));
|
||||
devs->decoderMpeg12->issueACmd(BitBashing<u32>(value));
|
||||
}
|
||||
return ipu;
|
||||
}
|
||||
os::vec MemoryPipe::dmaAddrCollector(u32 address, os::vec value, u64 size, bool ro) {
|
||||
os::vec MemoryPipe::dmaAddrCollector(u32 address, const os::vec& value, u64 size, bool ro) {
|
||||
os::vec from{};
|
||||
if (ro)
|
||||
from = controller->performRead(address);
|
||||
|
||||
return from;
|
||||
}
|
||||
std::array<GlobalRangeSpecs, 2> globalRanges{{
|
||||
os::vec MemoryPipe::iopSpecialRegs(u32 address, const os::vec& value, u64 size, bool ro) {
|
||||
u64 iopTimerIndex{};
|
||||
os::vec result{};
|
||||
switch (address) {
|
||||
}
|
||||
result = devs->mipsIop->timer->performTimerAccess(address, BitBashing<u32>(value), !ro);
|
||||
|
||||
return {};
|
||||
}
|
||||
std::array<GlobalRangeSpecs, 3> globalRanges{{
|
||||
{0x10002000, 0x10002030, MemoryPipe::IpuRelatedAddr},
|
||||
{0x10008000, 0x1000f000, MemoryPipe::DmaRelatedAddr}
|
||||
{0x10008000, 0x1000f000, MemoryPipe::DmaRelatedAddr},
|
||||
{0x1f801070, 0x1f801574, MemoryPipe::IopRelatedAddr},
|
||||
}};
|
||||
|
||||
void MemoryPipe::writeGlobal(u32 address, os::vec value, u64 size, PipeAccess dev) {
|
||||
pointer[0] = solveGlobal(address, dev);
|
||||
void MemoryPipe::writeGlobal(u32 address, const os::vec& value, u64 size, PipeAccess dev) {
|
||||
pointer = solveGlobal(address, dev);
|
||||
bool threat{};
|
||||
|
||||
ranges::for_each(globalRanges, [&](auto& region) {
|
||||
if (region.starts >= address && region.ends < address) {
|
||||
switch (region.funcId) {
|
||||
switch (region.function) {
|
||||
case IpuRelatedAddr:
|
||||
imageDecoderGlb(address, value, size, false); break;
|
||||
case DmaRelatedAddr:
|
||||
dmaAddrCollector(address, value, size, false); break;
|
||||
case IopRelatedAddr:
|
||||
iopSpecialRegs(address, value, size, false); break;
|
||||
}
|
||||
threat = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!threat && pointer[0]) {
|
||||
writeBack(pointer[0], value, size);
|
||||
}
|
||||
if (!threat && pointer && size == sizeof(u32))
|
||||
pointer.virtWrite<u32>(0, BitBashing<u32>(value));
|
||||
}
|
||||
|
||||
os::vec MemoryPipe::readGlobal(u32 address, u64 size, PipeAccess dev) {
|
||||
pointer[0] = solveGlobal(address, dev);
|
||||
pointer = solveGlobal(address, dev);
|
||||
bool threat{};
|
||||
os::vec result{};
|
||||
|
||||
ranges::for_each(globalRanges, [&](auto& region) {
|
||||
if (region.starts >= address && region.ends < address) {
|
||||
switch (region.funcId) {
|
||||
switch (region.function) {
|
||||
case IpuRelatedAddr:
|
||||
result = imageDecoderGlb(address, 0, size, true);
|
||||
result = imageDecoderGlb(address, {}, size, true);
|
||||
case DmaRelatedAddr:
|
||||
result = dmaAddrCollector(address, 0, size, true);
|
||||
result = dmaAddrCollector(address, {}, size, true);
|
||||
case IopRelatedAddr:
|
||||
result = iopSpecialRegs(address, {}, size, false); break;
|
||||
}
|
||||
threat = true;
|
||||
}
|
||||
});
|
||||
if (!threat && pointer[0]) {
|
||||
result = readBack(pointer[0], size);
|
||||
}
|
||||
if (!threat && pointer && size == sizeof(u32))
|
||||
result = pointer.virtRead<u32>();
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://www.psx-place.com/threads/ps2s-builtin-ps1-functions-documentation.26901/
|
||||
enum PsxMode { Psx2Only = 0, Psx1Compatibility = 0x8 };
|
||||
u32 hwIoCfg{Psx2Only};
|
||||
u32 sSbus{};
|
||||
u32 hole{};
|
||||
u32 hwIoCfg,
|
||||
sSbus,
|
||||
hole;
|
||||
|
||||
struct IopTimersCct {
|
||||
u32 counter, control, target;
|
||||
};
|
||||
std::array<IopTimersCct, 1> iopTimersArea {
|
||||
{{0x1f801120, 0x1f801124, 0x1f801128}}
|
||||
};
|
||||
void MemoryPipe::resetIoVariables() {
|
||||
hwIoCfg = Psx2Only;
|
||||
sSbus = {};
|
||||
hole = {};
|
||||
}
|
||||
|
||||
VirtualPointer MemoryPipe::iopHalLookup(u32 address) {
|
||||
switch (address) {
|
||||
@ -116,15 +130,7 @@ namespace cosmic::mio {
|
||||
// checking if the processor supports PS1 mode
|
||||
return &hwIoCfg;
|
||||
}
|
||||
ranges::for_each(iopTimersArea, [&](const auto& tXMap) {
|
||||
if (tXMap.counter == address) {
|
||||
}
|
||||
if (tXMap.control == address) {
|
||||
}
|
||||
if (tXMap.target == address) {
|
||||
}
|
||||
});
|
||||
return &hole;
|
||||
return {};
|
||||
}
|
||||
VirtualPointer MemoryPipe::directPointer(u32 address, PipeAccess dev) {
|
||||
switch (dev) {
|
||||
|
@ -23,12 +23,12 @@ namespace cosmic::mio {
|
||||
return BitCast<T>(pointer + address);
|
||||
}
|
||||
template<typename T>
|
||||
auto read(u32 address = 0) {
|
||||
return *as<T>(address);
|
||||
auto virtRead(u32 address = 0) {
|
||||
return *as<T*>(address);
|
||||
}
|
||||
template<typename T>
|
||||
void write(u32 address = 0, auto value = {}) {
|
||||
*as<T>(address) = value;
|
||||
void virtWrite(u32 address = 0, auto value = {}) {
|
||||
*as<T*>(address) = value;
|
||||
}
|
||||
VirtualPointer() = default;
|
||||
VirtualPointer(u8* addr) : pointer(addr) {
|
||||
@ -43,27 +43,17 @@ namespace cosmic::mio {
|
||||
u8* pointer{};
|
||||
};
|
||||
template<typename T = os::vec>
|
||||
T bitBashing(const os::vec& vec) {
|
||||
os::vec clb{0xff};
|
||||
switch (sizeof(T) * 8) {
|
||||
case 16: clb = 0xffff; break;
|
||||
case 32: clb = 0xffffffff; break;
|
||||
case 64: clb = 0xffffffffffffffff; break;
|
||||
case 128:
|
||||
// This will clean all bits to 0
|
||||
clb = {0xffffffffffffffff, 0xffffffffffffffff};
|
||||
}
|
||||
const os::vec cleaned{vec & clb};
|
||||
T BitBashing(const os::vec& vec) {
|
||||
if constexpr (std::is_same<T, u32>::value)
|
||||
return cleaned.to32(0);
|
||||
return vec.to32(0);
|
||||
if constexpr (std::is_same<T, os::vec>::value)
|
||||
return cleaned;
|
||||
return vec;
|
||||
return {};
|
||||
}
|
||||
class MemoryPipe {
|
||||
public:
|
||||
MemoryPipe(std::shared_ptr<console::VirtDevices>& devices);
|
||||
void writeGlobal(u32 address, os::vec value, u64 size, PipeAccess dev);
|
||||
void writeGlobal(u32 address, const os::vec& value, u64 size, PipeAccess dev);
|
||||
os::vec readGlobal(u32 address, u64 size, PipeAccess dev);
|
||||
|
||||
VirtualPointer solveGlobal(u32 address = 0, PipeAccess dev = CoreDevices);
|
||||
@ -71,50 +61,35 @@ namespace cosmic::mio {
|
||||
VirtualPointer directPointer(u32 address, PipeAccess dev);
|
||||
|
||||
std::shared_ptr<DmaController> controller;
|
||||
|
||||
os::vec readBack(VirtualPointer& virt, u64 size) {
|
||||
if (size == sizeof(u32))
|
||||
return virt.read<u32*>();
|
||||
return static_cast<u32>(0);
|
||||
}
|
||||
void writeBack(VirtualPointer& virt, const os::vec& value, u64 size) {
|
||||
if (size == sizeof(u32)) {
|
||||
virt.write<u32*>(0, bitBashing<u32>(value));
|
||||
}
|
||||
}
|
||||
void resetIoVariables();
|
||||
|
||||
enum MemoryOrderFuncId {
|
||||
IpuRelatedAddr,
|
||||
DmaRelatedAddr
|
||||
DmaRelatedAddr,
|
||||
IopRelatedAddr,
|
||||
};
|
||||
private:
|
||||
std::shared_ptr<console::VirtDevices> devs;
|
||||
VirtualPointer pointer[1];
|
||||
VirtualPointer pointer;
|
||||
|
||||
os::vec imageDecoderGlb(u32 address, os::vec value, u64 size, bool ro);
|
||||
os::vec dmaAddrCollector(u32 address, os::vec value, u64 size, bool ro);
|
||||
os::vec imageDecoderGlb(u32 address, const os::vec& value, u64 size, bool ro);
|
||||
os::vec dmaAddrCollector(u32 address, const os::vec& value, u64 size, bool ro);
|
||||
os::vec iopSpecialRegs(u32 address, const os::vec& value, u64 size, bool ro);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
[[gnu::always_inline]] auto PipeCraftPtr(
|
||||
std::shared_ptr<MemoryPipe>& mem,
|
||||
u32 address,
|
||||
PipeAccess dev = CoreDevices) {
|
||||
return mem->directPointer(address, dev).as<T>();
|
||||
auto PipeCraftPtr(std::shared_ptr<MemoryPipe>& pipe,
|
||||
u32 address, PipeAccess dev = CoreDevices) {
|
||||
return pipe->directPointer(address, dev).as<T>();
|
||||
}
|
||||
template <typename T>
|
||||
[[gnu::always_inline]] auto PipeRead(
|
||||
std::shared_ptr<MemoryPipe>& mem,
|
||||
u32 address,
|
||||
PipeAccess dev = CoreDevices) {
|
||||
return mem->readGlobal(address, sizeof(T), dev).as<T>();
|
||||
auto PipeRead(std::shared_ptr<MemoryPipe>& pipe,
|
||||
u32 address, PipeAccess dev = CoreDevices) {
|
||||
return pipe->readGlobal(address, sizeof(T), dev).as<T>();
|
||||
}
|
||||
template <typename T>
|
||||
[[gnu::always_inline]] void PipeWrite(
|
||||
std::shared_ptr<MemoryPipe>& mem,
|
||||
u32 address,
|
||||
os::vec value,
|
||||
PipeAccess dev = CoreDevices) {
|
||||
mem->writeGlobal(address, value, sizeof(T), dev);
|
||||
void PipeWrite(std::shared_ptr<MemoryPipe>& pipe,
|
||||
u32 address, const os::vec& value, PipeAccess dev = CoreDevices) {
|
||||
pipe->writeGlobal(address, value, sizeof(T), dev);
|
||||
}
|
||||
}
|
@ -89,13 +89,13 @@ namespace cosmic::vm {
|
||||
status.clearStatus();
|
||||
scheduler->resetCycles();
|
||||
|
||||
|
||||
// Resetting all co-processors
|
||||
mips->resetCore();
|
||||
gsGif->resetGif();
|
||||
gsCore->resetGraphics();
|
||||
|
||||
sharedPipe->controller->resetMa();
|
||||
sharedPipe->resetIoVariables();
|
||||
mpegDecoder->resetDecoder();
|
||||
mips->timer->resetTimers();
|
||||
|
||||
@ -108,7 +108,13 @@ namespace cosmic::vm {
|
||||
ioDma->resetIoDma();
|
||||
sound->resetSound();
|
||||
|
||||
// iop->iopMem->controller->mapped->iopSoftClean();
|
||||
#if !defined(NDEBUG)
|
||||
// Memory is assumed to be random content by a early moment before tbe 1ft boot stage,
|
||||
// but for debugging purposes, we will cleanup everything from now (at least the EE memory)
|
||||
iop->iopMem->controller->mapped->iopSoftClean();
|
||||
iop->iopMem->controller->mapped->sndSoftClean();
|
||||
iop->iopMem->controller->mapped->ramSoftClean();
|
||||
#endif
|
||||
}
|
||||
void EmuVm::dealWithSyscalls() {
|
||||
hle::SyscallOrigin origin{};
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <vm/sched_logical.h>
|
||||
#include <common/global.h>
|
||||
namespace cosmic::vm {
|
||||
constexpr u64 maxCycle{std::numeric_limits<u64>::max()};
|
||||
|
||||
void Scheduler::runTasks() {
|
||||
if (eeCycles.cycles < nearestEventCycle)
|
||||
return;
|
||||
@ -47,7 +49,7 @@ namespace cosmic::vm {
|
||||
iopCycles.cycles = 0;
|
||||
// iopCycles = {};
|
||||
|
||||
nearestEventCycle = std::numeric_limits<u64>::max();
|
||||
nearestEventCycle = maxCycle;
|
||||
std::list<EventSched> ee{};
|
||||
std::vector<TimerSched> te{};
|
||||
|
||||
@ -104,7 +106,7 @@ namespace cosmic::vm {
|
||||
|
||||
if (pause) {
|
||||
auto event{searchIdEvent(sid)};
|
||||
event->insideCycleCount = std::numeric_limits<u64>::max();
|
||||
event->insideCycleCount = maxCycle;
|
||||
} else {
|
||||
pickedTimer.lastUpdate = eeCycles.cycles;
|
||||
}
|
||||
@ -188,7 +190,6 @@ namespace cosmic::vm {
|
||||
}
|
||||
auto result{sid};
|
||||
result = {};
|
||||
constexpr u64 maxCycle{std::numeric_limits<u64>::max()};
|
||||
|
||||
if (!isEvent) {
|
||||
TimerSched timer{
|
||||
|
Loading…
Reference in New Issue
Block a user