mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-27 00:00:07 -06:00
camera: mostly working picture transfer infrastructure
This commit is contained in:
@ -31,9 +31,14 @@ Camera* Camera1; // 7A / selfie cam
|
|||||||
u16 ModuleCnt;
|
u16 ModuleCnt;
|
||||||
u16 Cnt;
|
u16 Cnt;
|
||||||
|
|
||||||
u8 FrameBuffer[640*480*4];
|
/*u8 FrameBuffer[640*480*4];
|
||||||
u32 FrameLength;
|
u32 FrameLength;
|
||||||
u32 TransferPos;
|
u32 TransferPos;*/
|
||||||
|
|
||||||
|
// pixel data buffer holds a maximum of 1024 words, regardless of how long scanlines are
|
||||||
|
u32 DataBuffer[1024];
|
||||||
|
u32 BufferReadPos, BufferWritePos;
|
||||||
|
Camera* CurCamera;
|
||||||
|
|
||||||
// note on camera data/etc intervals
|
// note on camera data/etc intervals
|
||||||
// on hardware those are likely affected by several factors
|
// on hardware those are likely affected by several factors
|
||||||
@ -66,9 +71,13 @@ void Reset()
|
|||||||
ModuleCnt = 0; // CHECKME
|
ModuleCnt = 0; // CHECKME
|
||||||
Cnt = 0;
|
Cnt = 0;
|
||||||
|
|
||||||
memset(FrameBuffer, 0, 640*480*4);
|
/*memset(FrameBuffer, 0, 640*480*4);
|
||||||
TransferPos = 0;
|
TransferPos = 0;
|
||||||
FrameLength = 256*192*2; // TODO: make it check frame size, data type, etc
|
FrameLength = 256*192*2;*/ // TODO: make it check frame size, data type, etc
|
||||||
|
memset(DataBuffer, 0, 1024*sizeof(u32));
|
||||||
|
BufferReadPos = 0;
|
||||||
|
BufferWritePos = 0;
|
||||||
|
CurCamera = nullptr;
|
||||||
|
|
||||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||||
}
|
}
|
||||||
@ -80,9 +89,9 @@ void DoSavestate(Savestate* file)
|
|||||||
file->Var16(&ModuleCnt);
|
file->Var16(&ModuleCnt);
|
||||||
file->Var16(&Cnt);
|
file->Var16(&Cnt);
|
||||||
|
|
||||||
file->VarArray(FrameBuffer, sizeof(FrameBuffer));
|
/*file->VarArray(FrameBuffer, sizeof(FrameBuffer));
|
||||||
file->Var32(&TransferPos);
|
file->Var32(&TransferPos);
|
||||||
file->Var32(&FrameLength);
|
file->Var32(&FrameLength);*/
|
||||||
|
|
||||||
Camera0->DoSavestate(file);
|
Camera0->DoSavestate(file);
|
||||||
Camera1->DoSavestate(file);
|
Camera1->DoSavestate(file);
|
||||||
@ -101,63 +110,60 @@ void IRQ(u32 param)
|
|||||||
|
|
||||||
if (activecam)
|
if (activecam)
|
||||||
{
|
{
|
||||||
RequestFrame(activecam->Num);
|
activecam->StartTransfer();
|
||||||
|
|
||||||
if (Cnt & (1<<11))
|
if (Cnt & (1<<11))
|
||||||
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
|
NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
|
||||||
|
|
||||||
if (Cnt & (1<<15))
|
if (Cnt & (1<<15))
|
||||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, Transfer, 0);
|
{
|
||||||
|
BufferReadPos = 0;
|
||||||
|
BufferWritePos = 0;
|
||||||
|
CurCamera = activecam;
|
||||||
|
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestFrame(u32 cam)
|
void TransferScanline(u32 line)
|
||||||
{
|
{
|
||||||
if (!(Cnt & (1<<13))) printf("CAMERA: !! REQUESTING YUV FRAME\n");
|
u32* dstbuf = &DataBuffer[BufferWritePos];
|
||||||
|
u32 maxlen = 1024 - BufferWritePos;
|
||||||
|
u32 datalen = CurCamera->TransferScanline(dstbuf, maxlen);
|
||||||
|
|
||||||
// TODO: picture size, data type, cropping, etc
|
// TODO HERE:
|
||||||
// generate test pattern
|
// convert to RGB5 if needed
|
||||||
// TODO: get picture from platform (actual camera, video file, whatever source)
|
// crop if needed
|
||||||
for (u32 y = 0; y < 192; y++)
|
// etc
|
||||||
|
|
||||||
|
// data overrun
|
||||||
|
if (datalen > maxlen)
|
||||||
|
Cnt |= (1<<4);
|
||||||
|
|
||||||
|
u32 numscan = Cnt & 0x000F;
|
||||||
|
if (line >= numscan)
|
||||||
{
|
{
|
||||||
for (u32 x = 0; x < 256; x++)
|
BufferReadPos = 0; // checkme
|
||||||
{
|
BufferWritePos = 0;
|
||||||
u16* px = (u16*)&FrameBuffer[((y*256) + x) * 2];
|
line = 0;
|
||||||
|
|
||||||
if ((x & 0x8) ^ (y & 0x8))
|
|
||||||
*px = 0x8000;
|
|
||||||
else
|
|
||||||
*px = 0xFC00 | ((y >> 3) << 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transfer(u32 pos)
|
|
||||||
{
|
|
||||||
u32 numscan = (Cnt & 0x000F) + 1;
|
|
||||||
u32 numpix = numscan * 256; // CHECKME
|
|
||||||
|
|
||||||
// TODO: present data
|
|
||||||
//printf("CAM TRANSFER POS=%d/%d\n", pos, 0x6000*2);
|
|
||||||
|
|
||||||
DSi::CheckNDMAs(0, 0x0B);
|
DSi::CheckNDMAs(0, 0x0B);
|
||||||
|
|
||||||
pos += numpix;
|
|
||||||
if (pos >= 0x6000*2) // HACK
|
|
||||||
{
|
|
||||||
// transfer done
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// keep going
|
BufferWritePos += datalen;
|
||||||
|
if (BufferWritePos > 1024) BufferWritePos = 1024;
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurCamera->TransferDone())
|
||||||
|
return;
|
||||||
|
|
||||||
// TODO: must be tweaked such that each block has enough time to transfer
|
// TODO: must be tweaked such that each block has enough time to transfer
|
||||||
u32 delay = numpix*2 + 16;
|
u32 delay = datalen*4 + 16;
|
||||||
|
|
||||||
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, Transfer, pos);
|
NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -187,20 +193,16 @@ u32 Read32(u32 addr)
|
|||||||
{
|
{
|
||||||
case 0x04004204:
|
case 0x04004204:
|
||||||
{
|
{
|
||||||
// TODO
|
u32 ret = DataBuffer[BufferReadPos];
|
||||||
return 0xFC00801F;
|
if (Cnt & (1<<15))
|
||||||
/*if (!(Cnt & (1<<15))) return 0; // CHECKME
|
|
||||||
u32 ret = *(u32*)&FrameBuffer[TransferPos];
|
|
||||||
TransferPos += 4;
|
|
||||||
if (TransferPos >= FrameLength) TransferPos = 0;
|
|
||||||
dorp += 4;
|
|
||||||
//if (dorp >= (256*4*2))
|
|
||||||
if (TransferPos == 0)
|
|
||||||
{
|
{
|
||||||
dorp = 0;
|
if (BufferReadPos < 1023)
|
||||||
Cnt &= ~(1<<4);
|
BufferReadPos++;
|
||||||
|
// CHECKME!!!!
|
||||||
|
// also presumably we should set bit4 in Cnt if there's no new data to be read
|
||||||
}
|
}
|
||||||
return ret;*/
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +218,7 @@ void Write8(u32 addr, u8 val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Write16(u32 addr, u16 val)
|
void Write16(u32 addr, u16 val)
|
||||||
{printf("ASS CAMERA REG: %08X %04X\n", addr, val);
|
{
|
||||||
switch (addr)
|
switch (addr)
|
||||||
{
|
{
|
||||||
case 0x04004200:
|
case 0x04004200:
|
||||||
@ -255,7 +257,12 @@ void Write16(u32 addr, u16 val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Cnt = (Cnt & oldmask) | (val & ~0x0020);
|
Cnt = (Cnt & oldmask) | (val & ~0x0020);
|
||||||
if (val & (1<<5)) Cnt &= ~(1<<4);
|
if (val & (1<<5))
|
||||||
|
{
|
||||||
|
Cnt &= ~(1<<4);
|
||||||
|
BufferReadPos = 0;
|
||||||
|
BufferWritePos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ((val & (1<<15)) && !(Cnt & (1<<15)))
|
if ((val & (1<<15)) && !(Cnt & (1<<15)))
|
||||||
{
|
{
|
||||||
@ -305,8 +312,6 @@ void Camera::DoSavestate(Savestate* file)
|
|||||||
file->Var16(&MiscCnt);
|
file->Var16(&MiscCnt);
|
||||||
|
|
||||||
file->Var16(&MCUAddr);
|
file->Var16(&MCUAddr);
|
||||||
// TODO: MCUData??
|
|
||||||
|
|
||||||
file->VarArray(MCURegs, 0x8000);
|
file->VarArray(MCURegs, 0x8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,10 +328,26 @@ void Camera::Reset()
|
|||||||
StandbyCnt = 0x4029; // checkme
|
StandbyCnt = 0x4029; // checkme
|
||||||
MiscCnt = 0;
|
MiscCnt = 0;
|
||||||
|
|
||||||
|
MCUAddr = 0;
|
||||||
memset(MCURegs, 0, 0x8000);
|
memset(MCURegs, 0, 0x8000);
|
||||||
|
|
||||||
// default state is preview mode (checkme)
|
// default state is preview mode (checkme)
|
||||||
MCURegs[0x2104] = 3;
|
MCURegs[0x2104] = 3;
|
||||||
|
|
||||||
|
TransferY = 0;
|
||||||
|
memset(FrameBuffer, 0, 640*480*sizeof(u32));
|
||||||
|
|
||||||
|
// test pattern
|
||||||
|
for (int y = 0; y < 480; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 640; x++)
|
||||||
|
{
|
||||||
|
u32 color = Num ? 0x00FF0000 : 0x000000FF;
|
||||||
|
if ((x & 0x10) ^ (y & 0x10)) color |= 0x0000FF00;
|
||||||
|
|
||||||
|
FrameBuffer[y*640 + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::IsActivated()
|
bool Camera::IsActivated()
|
||||||
@ -338,6 +359,105 @@ bool Camera::IsActivated()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Camera::StartTransfer()
|
||||||
|
{
|
||||||
|
TransferY = 0;
|
||||||
|
|
||||||
|
u8 state = MCURegs[0x2104];
|
||||||
|
if (state == 3) // preview
|
||||||
|
{
|
||||||
|
FrameWidth = *(u16*)&MCURegs[0x2703];
|
||||||
|
FrameHeight = *(u16*)&MCURegs[0x2705];
|
||||||
|
FrameReadMode = *(u16*)&MCURegs[0x2717];
|
||||||
|
FrameFormat = *(u16*)&MCURegs[0x2755];
|
||||||
|
}
|
||||||
|
else if (state == 7) // capture
|
||||||
|
{
|
||||||
|
FrameWidth = *(u16*)&MCURegs[0x2707];
|
||||||
|
FrameHeight = *(u16*)&MCURegs[0x2709];
|
||||||
|
FrameReadMode = *(u16*)&MCURegs[0x272D];
|
||||||
|
FrameFormat = *(u16*)&MCURegs[0x2757];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FrameWidth = 0;
|
||||||
|
FrameHeight = 0;
|
||||||
|
FrameReadMode = 0;
|
||||||
|
FrameFormat = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Camera::TransferDone()
|
||||||
|
{
|
||||||
|
return TransferY >= FrameHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Camera::TransferScanline(u32* buffer, int maxlen)
|
||||||
|
{
|
||||||
|
if (TransferY >= FrameHeight)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (FrameWidth > 640 || FrameHeight > 480 ||
|
||||||
|
FrameWidth < 2 || FrameHeight < 2 ||
|
||||||
|
(FrameWidth & 1))
|
||||||
|
{
|
||||||
|
// TODO work out something for these cases?
|
||||||
|
printf("CAM%d: invalid resolution %dx%d\n", Num, FrameWidth, FrameHeight);
|
||||||
|
//memset(buffer, 0, width*height*sizeof(u16));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: non-YUV pixel formats and all
|
||||||
|
|
||||||
|
int retlen = FrameWidth >> 1;
|
||||||
|
int sy = (TransferY * 480) / FrameHeight;
|
||||||
|
|
||||||
|
for (int dx = 0; dx < retlen; dx++)
|
||||||
|
{
|
||||||
|
if (dx >= maxlen) break;
|
||||||
|
|
||||||
|
// convert to YUV
|
||||||
|
// Y = 0.299R + 0.587G + 0.114B
|
||||||
|
// U = 0.492 (B-Y)
|
||||||
|
// V = 0.877 (R-Y)
|
||||||
|
|
||||||
|
int sx;
|
||||||
|
|
||||||
|
sx = ((dx*2) * 640) / FrameWidth;
|
||||||
|
u32 pixel1 = FrameBuffer[sy*640 + sx];
|
||||||
|
|
||||||
|
sx = ((dx*2 + 1) * 640) / FrameWidth;
|
||||||
|
u32 pixel2 = FrameBuffer[sy*640 + sx];
|
||||||
|
|
||||||
|
u32 r1 = (pixel1 >> 16) & 0xFF;
|
||||||
|
u32 g1 = (pixel1 >> 8) & 0xFF;
|
||||||
|
u32 b1 = pixel1 & 0xFF;
|
||||||
|
|
||||||
|
u32 r2 = (pixel1 >> 16) & 0xFF;
|
||||||
|
u32 g2 = (pixel1 >> 8) & 0xFF;
|
||||||
|
u32 b2 = pixel1 & 0xFF;
|
||||||
|
|
||||||
|
u32 y1 = ((r1 * 19595) + (g1 * 38470) + (b1 * 7471)) >> 16;
|
||||||
|
u32 u1 = ((b1 - y1) * 32244) >> 16;
|
||||||
|
u32 v1 = ((r1 - y1) * 57475) >> 16;
|
||||||
|
|
||||||
|
u32 y2 = ((r2 * 19595) + (g2 * 38470) + (b2 * 7471)) >> 16;
|
||||||
|
u32 u2 = ((b2 - y2) * 32244) >> 16;
|
||||||
|
u32 v2 = ((r2 - y2) * 57475) >> 16;
|
||||||
|
|
||||||
|
// huh
|
||||||
|
u1 = (u1 + u2) >> 1;
|
||||||
|
v1 = (v1 + v2) >> 1;
|
||||||
|
|
||||||
|
buffer[dx] = y1 | (u1 << 8) | (y2 << 16) | (u1 << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferY++;
|
||||||
|
|
||||||
|
return retlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Camera::I2C_Start()
|
void Camera::I2C_Start()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -496,18 +616,6 @@ void Camera::I2C_WriteReg(u16 addr, u16 val)
|
|||||||
u8 Camera::MCU_Read(u16 addr)
|
u8 Camera::MCU_Read(u16 addr)
|
||||||
{
|
{
|
||||||
addr &= 0x7FFF;
|
addr &= 0x7FFF;
|
||||||
printf("CAM%d MCU READ %04X -- %08X\n", Num, addr, NDS::GetPC(1));
|
|
||||||
|
|
||||||
switch (addr)
|
|
||||||
{
|
|
||||||
//case 0x2104: // SEQ_STATUS
|
|
||||||
// TODO!!!
|
|
||||||
// initial value 3
|
|
||||||
// value 7 after writing 2 to 2103
|
|
||||||
// preview mode: 256x512, DMA chunk size = 512
|
|
||||||
// capture mode: 640x480, DMA chunk size = 320
|
|
||||||
//return 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MCURegs[addr];
|
return MCURegs[addr];
|
||||||
}
|
}
|
||||||
@ -515,7 +623,6 @@ u8 Camera::MCU_Read(u16 addr)
|
|||||||
void Camera::MCU_Write(u16 addr, u8 val)
|
void Camera::MCU_Write(u16 addr, u8 val)
|
||||||
{
|
{
|
||||||
addr &= 0x7FFF;
|
addr &= 0x7FFF;
|
||||||
printf("CAM%d MCU WRITE %04X %02X\n", Num, addr, val);
|
|
||||||
|
|
||||||
switch (addr)
|
switch (addr)
|
||||||
{
|
{
|
||||||
@ -526,6 +633,9 @@ void Camera::MCU_Write(u16 addr, u8 val)
|
|||||||
else if (val != 5 && val != 6)
|
else if (val != 5 && val != 6)
|
||||||
printf("CAM%d: atypical SEQ_CMD %04X\n", Num, val);
|
printf("CAM%d: atypical SEQ_CMD %04X\n", Num, val);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0x2104: // SEQ_STATE, read-only
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MCURegs[addr] = val;
|
MCURegs[addr] = val;
|
||||||
|
@ -36,9 +36,8 @@ void Reset();
|
|||||||
void DoSavestate(Savestate* file);
|
void DoSavestate(Savestate* file);
|
||||||
|
|
||||||
void IRQ(u32 param);
|
void IRQ(u32 param);
|
||||||
void RequestFrame(u32 cam);
|
|
||||||
|
|
||||||
void Transfer(u32 pos);
|
void TransferScanline(u32 line);
|
||||||
|
|
||||||
u8 Read8(u32 addr);
|
u8 Read8(u32 addr);
|
||||||
u16 Read16(u32 addr);
|
u16 Read16(u32 addr);
|
||||||
@ -58,6 +57,12 @@ public:
|
|||||||
void Reset();
|
void Reset();
|
||||||
bool IsActivated();
|
bool IsActivated();
|
||||||
|
|
||||||
|
void StartTransfer();
|
||||||
|
bool TransferDone();
|
||||||
|
|
||||||
|
// lengths in words
|
||||||
|
int TransferScanline(u32* buffer, int maxlen);
|
||||||
|
|
||||||
void I2C_Start();
|
void I2C_Start();
|
||||||
u8 I2C_Read(bool last);
|
u8 I2C_Read(bool last);
|
||||||
void I2C_Write(u8 val, bool last);
|
void I2C_Write(u8 val, bool last);
|
||||||
@ -80,12 +85,15 @@ private:
|
|||||||
u16 MiscCnt;
|
u16 MiscCnt;
|
||||||
|
|
||||||
u16 MCUAddr;
|
u16 MCUAddr;
|
||||||
u16* MCUData;
|
|
||||||
|
|
||||||
u8 MCURegs[0x8000];
|
u8 MCURegs[0x8000];
|
||||||
|
|
||||||
u8 MCU_Read(u16 addr);
|
u8 MCU_Read(u16 addr);
|
||||||
void MCU_Write(u16 addr, u8 val);
|
void MCU_Write(u16 addr, u8 val);
|
||||||
|
|
||||||
|
u16 FrameWidth, FrameHeight;
|
||||||
|
u16 FrameReadMode, FrameFormat;
|
||||||
|
int TransferY;
|
||||||
|
u32 FrameBuffer[640*480];
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -723,7 +723,7 @@ bool DoSavestate_Scheduler(Savestate* file)
|
|||||||
DSi_SDHost::FinishTX,
|
DSi_SDHost::FinishTX,
|
||||||
DSi_NWifi::MSTimer,
|
DSi_NWifi::MSTimer,
|
||||||
DSi_CamModule::IRQ,
|
DSi_CamModule::IRQ,
|
||||||
DSi_CamModule::Transfer,
|
DSi_CamModule::TransferScanline,
|
||||||
DSi_DSP::DSPCatchUpU32,
|
DSi_DSP::DSPCatchUpU32,
|
||||||
|
|
||||||
nullptr
|
nullptr
|
||||||
|
Reference in New Issue
Block a user