mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 14:15:41 +12:00
Merge pull request #376 from wheremyfoodat/scheduler
More camera emulation
This commit is contained in:
commit
dcdafda658
4 changed files with 186 additions and 15 deletions
|
@ -12,22 +12,39 @@
|
|||
class Kernel;
|
||||
|
||||
class CAMService {
|
||||
using Event = std::optional<Handle>;
|
||||
|
||||
struct Port {
|
||||
Event bufferErrorInterruptevent = std::nullopt;
|
||||
u16 transferBytes;
|
||||
|
||||
void reset() {
|
||||
bufferErrorInterruptevent = std::nullopt;
|
||||
transferBytes = 256;
|
||||
}
|
||||
};
|
||||
|
||||
Handle handle = KernelHandles::CAM;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, camLogger)
|
||||
|
||||
using Event = std::optional<Handle>;
|
||||
static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH
|
||||
std::array<Event, portCount> bufferErrorInterruptEvents;
|
||||
static constexpr size_t portCount = 2;
|
||||
std::array<Port, portCount> ports;
|
||||
|
||||
// Service commands
|
||||
void driverInitialize(u32 messagePointer);
|
||||
void driverFinalize(u32 messagePointer);
|
||||
void getMaxLines(u32 messagePointer);
|
||||
void getBufferErrorInterruptEvent(u32 messagePointer);
|
||||
void getSuitableY2RCoefficients(u32 messagePointer);
|
||||
void getTransferBytes(u32 messagePointer);
|
||||
void setContrast(u32 messagePointer);
|
||||
void setFrameRate(u32 messagePointer);
|
||||
void setSize(u32 messagePointer);
|
||||
void setTransferLines(u32 messagePointer);
|
||||
void setTrimming(u32 messagePointer);
|
||||
void setTrimmingParamsCenter(u32 messagePointer);
|
||||
|
||||
public:
|
||||
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
|
|
|
@ -61,6 +61,7 @@ class FSService {
|
|||
void getFreeBytes(u32 messagePointer);
|
||||
void getFormatInfo(u32 messagePointer);
|
||||
void getPriority(u32 messagePointer);
|
||||
void getSdmcArchiveResource(u32 messagePointer);
|
||||
void getThisSaveDataSecureValue(u32 messagePointer);
|
||||
void theGameboyVCFunction(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
|
|
|
@ -6,24 +6,78 @@ namespace CAMCommands {
|
|||
enum : u32 {
|
||||
GetBufferErrorInterruptEvent = 0x00060040,
|
||||
DriverInitialize = 0x00390000,
|
||||
DriverFinalize = 0x003A0000,
|
||||
SetTransferLines = 0x00090100,
|
||||
GetMaxLines = 0x000A0080,
|
||||
GetTransferBytes = 0x000C0040,
|
||||
SetTrimming = 0x000E0080,
|
||||
SetTrimmingParamsCenter = 0x00120140,
|
||||
SetSize = 0x001F00C0, // Set size has different headers between cam:u and New3DS QTM module
|
||||
SetFrameRate = 0x00200080,
|
||||
SetContrast = 0x00230080,
|
||||
GetSuitableY2rStandardCoefficient = 0x00360000,
|
||||
};
|
||||
}
|
||||
|
||||
void CAMService::reset() { bufferErrorInterruptEvents.fill(std::nullopt); }
|
||||
// Helper struct for working with camera ports
|
||||
class PortSelect {
|
||||
u32 value;
|
||||
|
||||
public:
|
||||
PortSelect(u32 val) : value(val) {}
|
||||
bool isValid() const { return value < 4; }
|
||||
|
||||
bool isSinglePort() const {
|
||||
// 1 corresponds to the first camera port and 2 corresponds to the second port
|
||||
return value == 1 || value == 2;
|
||||
}
|
||||
|
||||
bool isBothPorts() const {
|
||||
// 3 corresponds to both ports
|
||||
return value == 3;
|
||||
}
|
||||
|
||||
// Returns the index of the camera port, assuming that it's only a single port
|
||||
int getSingleIndex() const {
|
||||
if (!isSinglePort()) [[unlikely]] {
|
||||
Helpers::panic("Camera: getSingleIndex called for port with invalid value");
|
||||
}
|
||||
|
||||
return value - 1;
|
||||
}
|
||||
|
||||
std::vector<int> getPortIndices() const {
|
||||
switch (value) {
|
||||
case 1: return {0}; // Only port 1
|
||||
case 2: return {1}; // Only port 2
|
||||
case 3: return {0, 1}; // Both port 1 and port 2
|
||||
default: return {}; // No ports or invalid ports
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void CAMService::reset() {
|
||||
for (auto& port : ports) {
|
||||
port.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CAMService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
switch (command) {
|
||||
case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break;
|
||||
case CAMCommands::DriverFinalize: driverFinalize(messagePointer); break;
|
||||
case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break;
|
||||
case CAMCommands::GetMaxLines: getMaxLines(messagePointer); break;
|
||||
case CAMCommands::GetSuitableY2rStandardCoefficient: getSuitableY2RCoefficients(messagePointer); break;
|
||||
case CAMCommands::GetTransferBytes: getTransferBytes(messagePointer); break;
|
||||
case CAMCommands::SetContrast: setContrast(messagePointer); break;
|
||||
case CAMCommands::SetFrameRate: setFrameRate(messagePointer); break;
|
||||
case CAMCommands::SetTransferLines: setTransferLines(messagePointer); break;
|
||||
case CAMCommands::SetTrimming: setTrimming(messagePointer); break;
|
||||
case CAMCommands::SetTrimmingParamsCenter: setTrimmingParamsCenter(messagePointer); break;
|
||||
case CAMCommands::SetSize: setSize(messagePointer); break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Unimplemented CAM service requested. Command: %08X\n", command);
|
||||
break;
|
||||
|
@ -36,6 +90,12 @@ void CAMService::driverInitialize(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void CAMService::driverFinalize(u32 messagePointer) {
|
||||
log("CAM::DriverFinalize\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x3A, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void CAMService::setContrast(u32 messagePointer) {
|
||||
const u32 cameraSelect = mem.read32(messagePointer + 4);
|
||||
const u32 contrast = mem.read32(messagePointer + 8);
|
||||
|
@ -47,12 +107,23 @@ void CAMService::setContrast(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void CAMService::setTransferLines(u32 messagePointer) {
|
||||
const u32 port = mem.read32(messagePointer + 4);
|
||||
const s16 lines = mem.read16(messagePointer + 8);
|
||||
const s16 width = mem.read16(messagePointer + 12);
|
||||
const s16 height = mem.read16(messagePointer + 16);
|
||||
const u32 portIndex = mem.read32(messagePointer + 4);
|
||||
const u16 lines = mem.read16(messagePointer + 8);
|
||||
const u16 width = mem.read16(messagePointer + 12);
|
||||
const u16 height = mem.read16(messagePointer + 16);
|
||||
const PortSelect port(portIndex);
|
||||
|
||||
log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", port, lines, width, height);
|
||||
if (port.isValid()) {
|
||||
const u32 transferBytes = lines * width * 2;
|
||||
|
||||
for (int i : port.getPortIndices()) {
|
||||
ports[i].transferBytes = transferBytes;
|
||||
}
|
||||
} else {
|
||||
Helpers::warn("CAM::SetTransferLines: Invalid port\n");
|
||||
}
|
||||
|
||||
log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", portIndex, lines, width, height);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
@ -68,6 +139,41 @@ void CAMService::setFrameRate(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void CAMService::setSize(u32 messagePointer) {
|
||||
const u32 cameraSelect = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const u32 context = mem.read32(messagePointer + 12);
|
||||
|
||||
log("CAM::SetSize (camera select = %d, size = %d, context = %d)\n", cameraSelect, size, context);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1F, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void CAMService::setTrimming(u32 messagePointer) {
|
||||
const u32 port = mem.read32(messagePointer + 4);
|
||||
const bool trim = mem.read8(messagePointer + 8) != 0;
|
||||
|
||||
log("CAM::SetTrimming (port = %d, trimming = %s)\n", port, trim ? "enabled" : "disabled");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0E, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void CAMService::setTrimmingParamsCenter(u32 messagePointer) {
|
||||
const u32 port = mem.read32(messagePointer + 4);
|
||||
const s16 trimWidth = s16(mem.read16(messagePointer + 8));
|
||||
const s16 trimHeight = s16(mem.read16(messagePointer + 12));
|
||||
const s16 cameraWidth = s16(mem.read16(messagePointer + 16));
|
||||
const s16 cameraHeight = s16(mem.read16(messagePointer + 20));
|
||||
|
||||
log("CAM::SetTrimmingParamsCenter (port = %d), trim size = (%d, %d), camera size = (%d, %d)\n", port, trimWidth, trimHeight, cameraWidth,
|
||||
cameraHeight);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
// Algorithm taken from Citra
|
||||
// https://github.com/citra-emu/citra/blob/master/src/core/hle/service/cam/cam.cpp#L465
|
||||
void CAMService::getMaxLines(u32 messagePointer) {
|
||||
|
@ -100,16 +206,40 @@ void CAMService::getMaxLines(u32 messagePointer) {
|
|||
}
|
||||
}
|
||||
|
||||
void CAMService::getSuitableY2RCoefficients(u32 messagePointer) {
|
||||
log("CAM::GetSuitableY2RCoefficients\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x36, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// Y2R standard coefficient value
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
}
|
||||
|
||||
void CAMService::getTransferBytes(u32 messagePointer) {
|
||||
const u32 portIndex = mem.read32(messagePointer + 4);
|
||||
const PortSelect port(portIndex);
|
||||
log("CAM::GetTransferBytes (port = %d)\n", portIndex);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0C, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
||||
if (port.isSinglePort()) {
|
||||
mem.write32(messagePointer + 8, ports[port.getSingleIndex()].transferBytes);
|
||||
} else {
|
||||
// TODO: This should return the proper error code
|
||||
Helpers::warn("CAM::GetTransferBytes: Invalid port index");
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
|
||||
const u32 port = mem.read32(messagePointer + 4);
|
||||
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", port);
|
||||
const u32 portIndex = mem.read32(messagePointer + 4);
|
||||
const PortSelect port(portIndex);
|
||||
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", portIndex);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2));
|
||||
|
||||
if (port >= portCount) {
|
||||
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
|
||||
} else {
|
||||
auto& event = bufferErrorInterruptEvents[port];
|
||||
if (port.isSinglePort()) {
|
||||
auto& event = ports[port.getSingleIndex()].bufferErrorInterruptevent;
|
||||
if (!event.has_value()) {
|
||||
event = kernel.makeEvent(ResetType::OneShot);
|
||||
}
|
||||
|
@ -117,5 +247,7 @@ void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0);
|
||||
mem.write32(messagePointer + 12, event.value());
|
||||
} else {
|
||||
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ namespace FSCommands {
|
|||
CloseArchive = 0x080E0080,
|
||||
FormatThisUserSaveData = 0x080F0180,
|
||||
GetFreeBytes = 0x08120080,
|
||||
GetSdmcArchiveResource = 0x08140000,
|
||||
IsSdmcDetected = 0x08170000,
|
||||
IsSdmcWritable = 0x08180000,
|
||||
CardSlotIsInserted = 0x08210000,
|
||||
|
@ -179,6 +180,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
|||
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
|
||||
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
|
||||
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
||||
case FSCommands::GetSdmcArchiveResource: getSdmcArchiveResource(messagePointer); break;
|
||||
case FSCommands::GetThisSaveDataSecureValue: getThisSaveDataSecureValue(messagePointer); break;
|
||||
case FSCommands::Initialize: initialize(messagePointer); break;
|
||||
case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break;
|
||||
|
@ -764,3 +766,22 @@ void FSService::renameFile(u32 messagePointer) {
|
|||
const HorizonResult res = sourceArchive->archive->renameFile(sourcePath, destPath);
|
||||
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
||||
}
|
||||
|
||||
void FSService::getSdmcArchiveResource(u32 messagePointer) {
|
||||
log("FS::GetSdmcArchiveResource"); // For the time being, return the same stubbed archive resource for every media type
|
||||
|
||||
static constexpr ArchiveResource resource = {
|
||||
.sectorSize = 512,
|
||||
.clusterSize = 16_KB,
|
||||
.partitionCapacityInClusters = 0x80000, // 0x80000 * 16 KB = 8GB
|
||||
.freeSpaceInClusters = 0x80000, // Same here
|
||||
};
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x814, 5, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
||||
mem.write32(messagePointer + 8, resource.sectorSize);
|
||||
mem.write32(messagePointer + 12, resource.clusterSize);
|
||||
mem.write32(messagePointer + 16, resource.partitionCapacityInClusters);
|
||||
mem.write32(messagePointer + 20, resource.freeSpaceInClusters);
|
||||
}
|
Loading…
Add table
Reference in a new issue