mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-26 15:50:00 -06:00
camera: revise interface behavior to be more accurate
* there are two FIFO buffers (finally fixes Let's Golf) * fix issues with camera start condition/cnt bit15 * add camera interface state to savestate
This commit is contained in:
@ -282,9 +282,9 @@ void DSi::DoSavestateExtra(Savestate* file)
|
|||||||
NDMAs[i].DoSavestate(file);
|
NDMAs[i].DoSavestate(file);
|
||||||
|
|
||||||
AES.DoSavestate(file);
|
AES.DoSavestate(file);
|
||||||
CamModule.DoSavestate(file);
|
|
||||||
DSP.DoSavestate(file);
|
DSP.DoSavestate(file);
|
||||||
I2C.DoSavestate(file);
|
I2C.DoSavestate(file);
|
||||||
|
CamModule.DoSavestate(file);
|
||||||
SDMMC.DoSavestate(file);
|
SDMMC.DoSavestate(file);
|
||||||
SDIO.DoSavestate(file);
|
SDIO.DoSavestate(file);
|
||||||
|
|
||||||
|
@ -67,12 +67,15 @@ void DSi_CamModule::Reset()
|
|||||||
CropStart = 0;
|
CropStart = 0;
|
||||||
CropEnd = 0;
|
CropEnd = 0;
|
||||||
|
|
||||||
memset(DataBuffer, 0, 512*sizeof(u32));
|
Transferring = false;
|
||||||
BufferReadPos = 0;
|
|
||||||
BufferWritePos = 0;
|
memset(PixelBuffer, 0, sizeof(PixelBuffer));
|
||||||
|
CurPixelBuffer = 0;
|
||||||
BufferNumLines = 0;
|
BufferNumLines = 0;
|
||||||
CurCamera = nullptr;
|
CurCamera = nullptr;
|
||||||
|
|
||||||
|
// TODO: ideally this should be started when a camera is active
|
||||||
|
// instead of just being a constant thing
|
||||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0);
|
DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +92,30 @@ void DSi_CamModule::DoSavestate(Savestate* file)
|
|||||||
file->Var16(&ModuleCnt);
|
file->Var16(&ModuleCnt);
|
||||||
file->Var16(&Cnt);
|
file->Var16(&Cnt);
|
||||||
|
|
||||||
/*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
|
file->Var32(&CropStart);
|
||||||
file->Var32(&TransferPos);
|
file->Var32(&CropEnd);
|
||||||
file->Var32(&FrameLength);*/
|
|
||||||
|
file->Bool32(&Transferring);
|
||||||
|
|
||||||
|
file->VarArray(&PixelBuffer[0].Data, 512);
|
||||||
|
file->Var32(&PixelBuffer[0].ReadPos);
|
||||||
|
file->Var32(&PixelBuffer[0].WritePos);
|
||||||
|
file->VarArray(&PixelBuffer[1].Data, 512);
|
||||||
|
file->Var32(&PixelBuffer[1].ReadPos);
|
||||||
|
file->Var32(&PixelBuffer[1].WritePos);
|
||||||
|
file->Var8(&CurPixelBuffer);
|
||||||
|
|
||||||
|
file->Var32(&BufferNumLines);
|
||||||
|
|
||||||
|
if (!file->Saving)
|
||||||
|
{
|
||||||
|
DSi_Camera* activecam = nullptr;
|
||||||
|
|
||||||
|
if (Camera0->IsActivated()) activecam = Camera0;
|
||||||
|
else if (Camera1->IsActivated()) activecam = Camera1;
|
||||||
|
|
||||||
|
CurCamera = activecam;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,14 +135,8 @@ void DSi_CamModule::IRQ(u32 param)
|
|||||||
if (Cnt & (1<<11))
|
if (Cnt & (1<<11))
|
||||||
DSi.SetIRQ(0, IRQ_DSi_Camera);
|
DSi.SetIRQ(0, IRQ_DSi_Camera);
|
||||||
|
|
||||||
if (Cnt & (1<<15))
|
CurCamera = activecam;
|
||||||
{
|
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
|
||||||
BufferReadPos = 0;
|
|
||||||
BufferWritePos = 0;
|
|
||||||
BufferNumLines = 0;
|
|
||||||
CurCamera = activecam;
|
|
||||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0);
|
DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0);
|
||||||
@ -126,8 +144,21 @@ void DSi_CamModule::IRQ(u32 param)
|
|||||||
|
|
||||||
void DSi_CamModule::TransferScanline(u32 line)
|
void DSi_CamModule::TransferScanline(u32 line)
|
||||||
{
|
{
|
||||||
u32* dstbuf = &DataBuffer[BufferWritePos];
|
if (line == 0)
|
||||||
int maxlen = 512 - BufferWritePos;
|
{
|
||||||
|
if (!(Cnt & (1<<15)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
BufferNumLines = 0;
|
||||||
|
Transferring = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Cnt & (1<<4))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer];
|
||||||
|
u32* dstbuf = &buffer->Data[buffer->WritePos];
|
||||||
|
int maxlen = 512 - buffer->WritePos;
|
||||||
|
|
||||||
u32 tmpbuf[512];
|
u32 tmpbuf[512];
|
||||||
int lines_next;
|
int lines_next;
|
||||||
@ -138,6 +169,7 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||||||
|
|
||||||
int copystart = 0;
|
int copystart = 0;
|
||||||
int copylen = datalen;
|
int copylen = datalen;
|
||||||
|
bool line_last = false;
|
||||||
|
|
||||||
if (Cnt & (1<<14))
|
if (Cnt & (1<<14))
|
||||||
{
|
{
|
||||||
@ -146,7 +178,10 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||||||
int ystart = (CropStart >> 16) & 0x1FF;
|
int ystart = (CropStart >> 16) & 0x1FF;
|
||||||
int yend = (CropEnd >> 16) & 0x1FF;
|
int yend = (CropEnd >> 16) & 0x1FF;
|
||||||
if (line < ystart || line > yend)
|
if (line < ystart || line > yend)
|
||||||
|
{
|
||||||
|
if (line == yend+1) line_last = true;
|
||||||
goto skip_line;
|
goto skip_line;
|
||||||
|
}
|
||||||
|
|
||||||
int xstart = (CropStart >> 1) & 0x1FF;
|
int xstart = (CropStart >> 1) & 0x1FF;
|
||||||
int xend = (CropEnd >> 1) & 0x1FF;
|
int xend = (CropEnd >> 1) & 0x1FF;
|
||||||
@ -163,7 +198,6 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||||||
if (copylen > maxlen)
|
if (copylen > maxlen)
|
||||||
{
|
{
|
||||||
copylen = maxlen;
|
copylen = maxlen;
|
||||||
Cnt |= (1<<4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Cnt & (1<<13))
|
if (Cnt & (1<<13))
|
||||||
@ -205,40 +239,63 @@ void DSi_CamModule::TransferScanline(u32 line)
|
|||||||
memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32));
|
memcpy(dstbuf, &tmpbuf[copystart], copylen*sizeof(u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer->WritePos += copylen;
|
||||||
|
if (buffer->WritePos > 512) buffer->WritePos = 512;
|
||||||
|
|
||||||
numscan = Cnt & 0x000F;
|
numscan = Cnt & 0x000F;
|
||||||
if (BufferNumLines >= numscan)
|
if (BufferNumLines >= numscan)
|
||||||
{
|
{
|
||||||
BufferReadPos = 0; // checkme
|
|
||||||
BufferWritePos = 0;
|
|
||||||
BufferNumLines = 0;
|
BufferNumLines = 0;
|
||||||
DSi.CheckNDMAs(0, 0x0B);
|
SwapPixelBuffers();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BufferWritePos += copylen;
|
|
||||||
if (BufferWritePos > 512) BufferWritePos = 512;
|
|
||||||
BufferNumLines++;
|
BufferNumLines++;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_line:
|
skip_line:
|
||||||
if (CurCamera->TransferDone())
|
bool done = CurCamera->TransferDone();
|
||||||
|
if (done || line_last)
|
||||||
{
|
{
|
||||||
// when the frame is finished, transfer any remaining data if needed
|
// when the frame is finished, transfer any remaining data if needed
|
||||||
// (if the frame height isn't a multiple of the DMA interval)
|
// (if the frame height isn't a multiple of the DMA interval)
|
||||||
if (BufferNumLines > 0)
|
if (BufferNumLines > 0)
|
||||||
{
|
{
|
||||||
BufferReadPos = 0;
|
|
||||||
BufferWritePos = 0;
|
|
||||||
BufferNumLines = 0;
|
BufferNumLines = 0;
|
||||||
DSi.CheckNDMAs(0, 0x0B);
|
SwapPixelBuffers();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done)
|
||||||
|
{
|
||||||
|
Transferring = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DSi_CamModule::SwapPixelBuffers()
|
||||||
|
{
|
||||||
|
// pixel buffers are swapped every time a buffer is filled (ie. when the DMA interval is reached)
|
||||||
|
// the swap fails if the other buffer isn't empty
|
||||||
|
|
||||||
|
sPixelBuffer* otherbuf = &PixelBuffer[CurPixelBuffer ^ 1];
|
||||||
|
if (otherbuf->ReadPos < otherbuf->WritePos)
|
||||||
|
{
|
||||||
|
// overrun
|
||||||
|
Cnt |= (1<<4);
|
||||||
|
Transferring = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PixelBuffer[CurPixelBuffer].ReadPos = 0;
|
||||||
|
otherbuf->WritePos = 0;
|
||||||
|
CurPixelBuffer ^= 1;
|
||||||
|
DSi.CheckNDMAs(0, 0x0B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
u8 DSi_CamModule::Read8(u32 addr)
|
u8 DSi_CamModule::Read8(u32 addr)
|
||||||
{
|
{
|
||||||
@ -253,7 +310,7 @@ u16 DSi_CamModule::Read16(u32 addr)
|
|||||||
switch (addr)
|
switch (addr)
|
||||||
{
|
{
|
||||||
case 0x04004200: return ModuleCnt;
|
case 0x04004200: return ModuleCnt;
|
||||||
case 0x04004202: return Cnt;
|
case 0x04004202: return Cnt | (Transferring ? 0x8000 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr);
|
Log(LogLevel::Debug, "unknown DSi cam read16 %08X\n", addr);
|
||||||
@ -266,13 +323,12 @@ u32 DSi_CamModule::Read32(u32 addr)
|
|||||||
{
|
{
|
||||||
case 0x04004204:
|
case 0x04004204:
|
||||||
{
|
{
|
||||||
u32 ret = DataBuffer[BufferReadPos];
|
sPixelBuffer* buffer = &PixelBuffer[CurPixelBuffer ^ 1];
|
||||||
|
u32 ret = buffer->Data[buffer->ReadPos];
|
||||||
if (Cnt & (1<<15))
|
if (Cnt & (1<<15))
|
||||||
{
|
{
|
||||||
if (BufferReadPos < 511)
|
if (buffer->ReadPos < buffer->WritePos)
|
||||||
BufferReadPos++;
|
buffer->ReadPos++;
|
||||||
// CHECKME!!!!
|
|
||||||
// also presumably we should set bit4 in Cnt if there's no new data to be read
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -308,6 +364,7 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||||||
// CHECKME
|
// CHECKME
|
||||||
|
|
||||||
Cnt = 0;
|
Cnt = 0;
|
||||||
|
Transferring = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
|
if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
|
||||||
@ -319,12 +376,9 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||||||
|
|
||||||
case 0x04004202:
|
case 0x04004202:
|
||||||
{
|
{
|
||||||
// TODO: during a transfer, clearing bit15 does not reflect immediately
|
|
||||||
// maybe it needs to finish the trasnfer or atleast the current block
|
|
||||||
|
|
||||||
// checkme
|
// checkme
|
||||||
u16 oldmask;
|
u16 oldmask;
|
||||||
if (Cnt & 0x8000)
|
if ((Cnt & 0x8000) || Transferring)
|
||||||
{
|
{
|
||||||
val &= 0x8F20;
|
val &= 0x8F20;
|
||||||
oldmask = 0x601F;
|
oldmask = 0x601F;
|
||||||
@ -339,14 +393,8 @@ void DSi_CamModule::Write16(u32 addr, u16 val)
|
|||||||
if (val & (1<<5))
|
if (val & (1<<5))
|
||||||
{
|
{
|
||||||
Cnt &= ~(1<<4);
|
Cnt &= ~(1<<4);
|
||||||
BufferReadPos = 0;
|
memset(PixelBuffer, 0, sizeof(PixelBuffer));
|
||||||
BufferWritePos = 0;
|
CurPixelBuffer = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if ((val & (1<<15)) && !(Cnt & (1<<15)))
|
|
||||||
{
|
|
||||||
// start transfer
|
|
||||||
//DSi::CheckNDMAs(0, 0x0B);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -118,15 +118,25 @@ private:
|
|||||||
|
|
||||||
u32 CropStart, CropEnd;
|
u32 CropStart, CropEnd;
|
||||||
|
|
||||||
// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are
|
bool Transferring;
|
||||||
u32 DataBuffer[512];
|
|
||||||
u32 BufferReadPos, BufferWritePos;
|
// pixel data buffers hold a maximum of 512 words, regardless of how long scanlines are
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u32 Data[512];
|
||||||
|
u32 ReadPos, WritePos;
|
||||||
|
|
||||||
|
} sPixelBuffer;
|
||||||
|
sPixelBuffer PixelBuffer[2];
|
||||||
|
u8 CurPixelBuffer;
|
||||||
u32 BufferNumLines;
|
u32 BufferNumLines;
|
||||||
DSi_Camera* CurCamera;
|
DSi_Camera* CurCamera;
|
||||||
|
|
||||||
static const u32 kIRQInterval;
|
static const u32 kIRQInterval;
|
||||||
static const u32 kScanlineTime;
|
static const u32 kScanlineTime;
|
||||||
static const u32 kTransferStart;
|
static const u32 kTransferStart;
|
||||||
|
|
||||||
|
void SwapPixelBuffers();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
11
src/NDS.cpp
11
src/NDS.cpp
@ -1883,7 +1883,7 @@ void NDS::debug(u32 param)
|
|||||||
//for (int i = 0; i < 9; i++)
|
//for (int i = 0; i < 9; i++)
|
||||||
// printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]);
|
// printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]);
|
||||||
|
|
||||||
Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write);
|
/*Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write);
|
||||||
Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit);
|
Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit);
|
||||||
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
|
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
|
||||||
{
|
{
|
||||||
@ -1900,20 +1900,21 @@ void NDS::debug(u32 param)
|
|||||||
u32 val = NDS::ARM7Read32(i);
|
u32 val = NDS::ARM7Read32(i);
|
||||||
Platform::FileWrite(&val, 4, 1, shit);
|
Platform::FileWrite(&val, 4, 1, shit);
|
||||||
}
|
}
|
||||||
Platform::CloseFile(shit);
|
Platform::CloseFile(shit);*/
|
||||||
|
|
||||||
/*FILE*
|
/*FILE*
|
||||||
shit = fopen("debug/directboot9.bin", "wb");
|
shit = fopen("debug/camera9.bin", "wb");
|
||||||
|
fwrite(ARM9.ITCM, 0x8000, 1, shit);
|
||||||
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
|
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
|
||||||
{
|
{
|
||||||
u32 val = DSi::ARM9Read32(i);
|
u32 val = ARM9Read32(i);
|
||||||
fwrite(&val, 4, 1, shit);
|
fwrite(&val, 4, 1, shit);
|
||||||
}
|
}
|
||||||
fclose(shit);
|
fclose(shit);
|
||||||
shit = fopen("debug/camera7.bin", "wb");
|
shit = fopen("debug/camera7.bin", "wb");
|
||||||
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
|
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
|
||||||
{
|
{
|
||||||
u32 val = DSi::ARM7Read32(i);
|
u32 val = ARM7Read32(i);
|
||||||
fwrite(&val, 4, 1, shit);
|
fwrite(&val, 4, 1, shit);
|
||||||
}
|
}
|
||||||
fclose(shit);*/
|
fclose(shit);*/
|
||||||
|
Reference in New Issue
Block a user