mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45: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 Kernel;
|
||||||
|
|
||||||
class CAMService {
|
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;
|
Handle handle = KernelHandles::CAM;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
Kernel& kernel;
|
Kernel& kernel;
|
||||||
MAKE_LOG_FUNCTION(log, camLogger)
|
MAKE_LOG_FUNCTION(log, camLogger)
|
||||||
|
|
||||||
using Event = std::optional<Handle>;
|
static constexpr size_t portCount = 2;
|
||||||
static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH
|
std::array<Port, portCount> ports;
|
||||||
std::array<Event, portCount> bufferErrorInterruptEvents;
|
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
void driverInitialize(u32 messagePointer);
|
void driverInitialize(u32 messagePointer);
|
||||||
|
void driverFinalize(u32 messagePointer);
|
||||||
void getMaxLines(u32 messagePointer);
|
void getMaxLines(u32 messagePointer);
|
||||||
void getBufferErrorInterruptEvent(u32 messagePointer);
|
void getBufferErrorInterruptEvent(u32 messagePointer);
|
||||||
|
void getSuitableY2RCoefficients(u32 messagePointer);
|
||||||
|
void getTransferBytes(u32 messagePointer);
|
||||||
void setContrast(u32 messagePointer);
|
void setContrast(u32 messagePointer);
|
||||||
void setFrameRate(u32 messagePointer);
|
void setFrameRate(u32 messagePointer);
|
||||||
|
void setSize(u32 messagePointer);
|
||||||
void setTransferLines(u32 messagePointer);
|
void setTransferLines(u32 messagePointer);
|
||||||
|
void setTrimming(u32 messagePointer);
|
||||||
|
void setTrimmingParamsCenter(u32 messagePointer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||||
|
|
|
@ -61,6 +61,7 @@ class FSService {
|
||||||
void getFreeBytes(u32 messagePointer);
|
void getFreeBytes(u32 messagePointer);
|
||||||
void getFormatInfo(u32 messagePointer);
|
void getFormatInfo(u32 messagePointer);
|
||||||
void getPriority(u32 messagePointer);
|
void getPriority(u32 messagePointer);
|
||||||
|
void getSdmcArchiveResource(u32 messagePointer);
|
||||||
void getThisSaveDataSecureValue(u32 messagePointer);
|
void getThisSaveDataSecureValue(u32 messagePointer);
|
||||||
void theGameboyVCFunction(u32 messagePointer);
|
void theGameboyVCFunction(u32 messagePointer);
|
||||||
void initialize(u32 messagePointer);
|
void initialize(u32 messagePointer);
|
||||||
|
|
|
@ -6,24 +6,78 @@ namespace CAMCommands {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
GetBufferErrorInterruptEvent = 0x00060040,
|
GetBufferErrorInterruptEvent = 0x00060040,
|
||||||
DriverInitialize = 0x00390000,
|
DriverInitialize = 0x00390000,
|
||||||
|
DriverFinalize = 0x003A0000,
|
||||||
SetTransferLines = 0x00090100,
|
SetTransferLines = 0x00090100,
|
||||||
GetMaxLines = 0x000A0080,
|
GetMaxLines = 0x000A0080,
|
||||||
|
GetTransferBytes = 0x000C0040,
|
||||||
|
SetTrimming = 0x000E0080,
|
||||||
|
SetTrimmingParamsCenter = 0x00120140,
|
||||||
|
SetSize = 0x001F00C0, // Set size has different headers between cam:u and New3DS QTM module
|
||||||
SetFrameRate = 0x00200080,
|
SetFrameRate = 0x00200080,
|
||||||
SetContrast = 0x00230080,
|
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) {
|
void CAMService::handleSyncRequest(u32 messagePointer) {
|
||||||
const u32 command = mem.read32(messagePointer);
|
const u32 command = mem.read32(messagePointer);
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break;
|
case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break;
|
||||||
|
case CAMCommands::DriverFinalize: driverFinalize(messagePointer); break;
|
||||||
case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break;
|
case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break;
|
||||||
case CAMCommands::GetMaxLines: getMaxLines(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::SetContrast: setContrast(messagePointer); break;
|
||||||
case CAMCommands::SetFrameRate: setFrameRate(messagePointer); break;
|
case CAMCommands::SetFrameRate: setFrameRate(messagePointer); break;
|
||||||
case CAMCommands::SetTransferLines: setTransferLines(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:
|
default:
|
||||||
Helpers::panic("Unimplemented CAM service requested. Command: %08X\n", command);
|
Helpers::panic("Unimplemented CAM service requested. Command: %08X\n", command);
|
||||||
break;
|
break;
|
||||||
|
@ -36,6 +90,12 @@ void CAMService::driverInitialize(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
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) {
|
void CAMService::setContrast(u32 messagePointer) {
|
||||||
const u32 cameraSelect = mem.read32(messagePointer + 4);
|
const u32 cameraSelect = mem.read32(messagePointer + 4);
|
||||||
const u32 contrast = mem.read32(messagePointer + 8);
|
const u32 contrast = mem.read32(messagePointer + 8);
|
||||||
|
@ -47,12 +107,23 @@ void CAMService::setContrast(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAMService::setTransferLines(u32 messagePointer) {
|
void CAMService::setTransferLines(u32 messagePointer) {
|
||||||
const u32 port = mem.read32(messagePointer + 4);
|
const u32 portIndex = mem.read32(messagePointer + 4);
|
||||||
const s16 lines = mem.read16(messagePointer + 8);
|
const u16 lines = mem.read16(messagePointer + 8);
|
||||||
const s16 width = mem.read16(messagePointer + 12);
|
const u16 width = mem.read16(messagePointer + 12);
|
||||||
const s16 height = mem.read16(messagePointer + 16);
|
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, IPC::responseHeader(0x9, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
@ -68,6 +139,41 @@ void CAMService::setFrameRate(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
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
|
// Algorithm taken from Citra
|
||||||
// https://github.com/citra-emu/citra/blob/master/src/core/hle/service/cam/cam.cpp#L465
|
// https://github.com/citra-emu/citra/blob/master/src/core/hle/service/cam/cam.cpp#L465
|
||||||
void CAMService::getMaxLines(u32 messagePointer) {
|
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) {
|
void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
|
||||||
const u32 port = mem.read32(messagePointer + 4);
|
const u32 portIndex = mem.read32(messagePointer + 4);
|
||||||
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", port);
|
const PortSelect port(portIndex);
|
||||||
|
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", portIndex);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2));
|
||||||
|
|
||||||
if (port >= portCount) {
|
if (port.isSinglePort()) {
|
||||||
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
|
auto& event = ports[port.getSingleIndex()].bufferErrorInterruptevent;
|
||||||
} else {
|
|
||||||
auto& event = bufferErrorInterruptEvents[port];
|
|
||||||
if (!event.has_value()) {
|
if (!event.has_value()) {
|
||||||
event = kernel.makeEvent(ResetType::OneShot);
|
event = kernel.makeEvent(ResetType::OneShot);
|
||||||
}
|
}
|
||||||
|
@ -117,5 +247,7 @@ void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, 0);
|
mem.write32(messagePointer + 8, 0);
|
||||||
mem.write32(messagePointer + 12, event.value());
|
mem.write32(messagePointer + 12, event.value());
|
||||||
|
} else {
|
||||||
|
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,7 @@ namespace FSCommands {
|
||||||
CloseArchive = 0x080E0080,
|
CloseArchive = 0x080E0080,
|
||||||
FormatThisUserSaveData = 0x080F0180,
|
FormatThisUserSaveData = 0x080F0180,
|
||||||
GetFreeBytes = 0x08120080,
|
GetFreeBytes = 0x08120080,
|
||||||
|
GetSdmcArchiveResource = 0x08140000,
|
||||||
IsSdmcDetected = 0x08170000,
|
IsSdmcDetected = 0x08170000,
|
||||||
IsSdmcWritable = 0x08180000,
|
IsSdmcWritable = 0x08180000,
|
||||||
CardSlotIsInserted = 0x08210000,
|
CardSlotIsInserted = 0x08210000,
|
||||||
|
@ -179,6 +180,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
||||||
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
|
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
|
||||||
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
|
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
|
||||||
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
||||||
|
case FSCommands::GetSdmcArchiveResource: getSdmcArchiveResource(messagePointer); break;
|
||||||
case FSCommands::GetThisSaveDataSecureValue: getThisSaveDataSecureValue(messagePointer); break;
|
case FSCommands::GetThisSaveDataSecureValue: getThisSaveDataSecureValue(messagePointer); break;
|
||||||
case FSCommands::Initialize: initialize(messagePointer); break;
|
case FSCommands::Initialize: initialize(messagePointer); break;
|
||||||
case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(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);
|
const HorizonResult res = sourceArchive->archive->renameFile(sourcePath, destPath);
|
||||||
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
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