aboutsummaryrefslogtreecommitdiff
path: root/src/DSi_Camera.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DSi_Camera.cpp')
-rw-r--r--src/DSi_Camera.cpp263
1 files changed, 253 insertions, 10 deletions
diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp
index 56cba1c..79cfe3f 100644
--- a/src/DSi_Camera.cpp
+++ b/src/DSi_Camera.cpp
@@ -18,12 +18,28 @@
#include <stdio.h>
#include <string.h>
+#include "DSi.h"
#include "DSi_Camera.h"
DSi_Camera* DSi_Camera0; // 78 / facing outside
DSi_Camera* DSi_Camera1; // 7A / selfie cam
+u16 DSi_Camera::ModuleCnt;
+u16 DSi_Camera::Cnt;
+
+u8 DSi_Camera::FrameBuffer[640*480*4];
+u32 DSi_Camera::FrameLength;
+u32 DSi_Camera::TransferPos;
+
+// note on camera data/etc intervals
+// on hardware those are likely affected by several factors
+// namely, how long cameras take to process frames
+// camera IRQ is fired at roughly 15FPS with default config
+
+const u32 kIRQInterval = 1120000; // ~30 FPS
+const u32 kTransferStart = 60000;
+
bool DSi_Camera::Init()
{
@@ -43,6 +59,87 @@ void DSi_Camera::Reset()
{
DSi_Camera0->ResetCam();
DSi_Camera1->ResetCam();
+
+ ModuleCnt = 0; // CHECKME
+ Cnt = 0;
+
+ memset(FrameBuffer, 0, 640*480*4);
+ TransferPos = 0;
+ FrameLength = 256*192*2; // TODO: make it check frame size, data type, etc
+
+ NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
+}
+
+
+void DSi_Camera::IRQ(u32 param)
+{
+ DSi_Camera* activecam = nullptr;
+
+ // TODO: check which camera has priority if both are activated
+ // (or does it just jumble both data sources together, like it
+ // does for, say, overlapping VRAM?)
+ if (DSi_Camera0->IsActivated()) activecam = DSi_Camera0;
+ else if (DSi_Camera1->IsActivated()) activecam = DSi_Camera1;
+
+ if (activecam)
+ {
+ RequestFrame(activecam->Num);
+
+ if (Cnt & (1<<11))
+ NDS::SetIRQ(0, NDS::IRQ_DSi_Camera);
+
+ if (Cnt & (1<<15))
+ NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, Transfer, 0);
+ }
+
+ NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0);
+}
+
+void DSi_Camera::RequestFrame(u32 cam)
+{
+ if (!(Cnt & (1<<13))) printf("CAMERA: !! REQUESTING YUV FRAME\n");
+
+ // TODO: picture size, data type, cropping, etc
+ // generate test pattern
+ // TODO: get picture from platform (actual camera, video file, whatever source)
+ for (u32 y = 0; y < 192; y++)
+ {
+ for (u32 x = 0; x < 256; x++)
+ {
+ u16* px = (u16*)&FrameBuffer[((y*256) + x) * 2];
+
+ if ((x & 0x8) ^ (y & 0x8))
+ *px = 0x8000;
+ else
+ *px = 0xFC00 | ((y >> 3) << 5);
+ }
+ }
+}
+
+void DSi_Camera::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);
+
+ pos += numpix;
+ if (pos >= 0x6000*2) // HACK
+ {
+ // transfer done
+ }
+ else
+ {
+ // keep going
+
+ // TODO: must be tweaked such that each block has enough time to transfer
+ u32 delay = numpix*2 + 16;
+
+ NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, Transfer, pos);
+ }
}
@@ -62,16 +159,28 @@ void DSi_Camera::ResetCam()
RegAddr = 0;
RegData = 0;
- PLLCnt = 0;
+ PLLDiv = 0x0366;
+ PLLPDiv = 0x00F5;
+ PLLCnt = 0x21F9;
+ ClocksCnt = 0;
StandbyCnt = 0x4029; // checkme
+ MiscCnt = 0;
+}
+
+bool DSi_Camera::IsActivated()
+{
+ if (StandbyCnt & (1<<14)) return false; // standby
+ if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled
+
+ return true;
}
-void DSi_Camera::Start()
+void DSi_Camera::I2C_Start()
{
}
-u8 DSi_Camera::Read(bool last)
+u8 DSi_Camera::I2C_Read(bool last)
{
u8 ret;
@@ -89,7 +198,7 @@ u8 DSi_Camera::Read(bool last)
}
else
{
- RegData = ReadReg(RegAddr);
+ RegData = I2C_ReadReg(RegAddr);
ret = RegData >> 8;
}
}
@@ -100,7 +209,7 @@ u8 DSi_Camera::Read(bool last)
return ret;
}
-void DSi_Camera::Write(u8 val, bool last)
+void DSi_Camera::I2C_Write(u8 val, bool last)
{
if (DataPos < 2)
{
@@ -116,7 +225,7 @@ void DSi_Camera::Write(u8 val, bool last)
if (DataPos & 0x1)
{
RegData |= val;
- WriteReg(RegAddr, RegData);
+ I2C_WriteReg(RegAddr, RegData);
RegAddr += 2; // checkme
}
else
@@ -129,38 +238,172 @@ void DSi_Camera::Write(u8 val, bool last)
else DataPos++;
}
-u16 DSi_Camera::ReadReg(u16 addr)
+u16 DSi_Camera::I2C_ReadReg(u16 addr)
{
switch (addr)
{
case 0x0000: return 0x2280; // chip ID
+ case 0x0010: return PLLDiv;
+ case 0x0012: return PLLPDiv;
case 0x0014: return PLLCnt;
+ case 0x0016: return ClocksCnt;
case 0x0018: return StandbyCnt;
+ case 0x001A: return MiscCnt;
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
}
- //printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
+ if(Num==1)printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
return 0;
}
-void DSi_Camera::WriteReg(u16 addr, u16 val)
+void DSi_Camera::I2C_WriteReg(u16 addr, u16 val)
{
switch (addr)
{
+ case 0x0010:
+ PLLDiv = val & 0x3FFF;
+ return;
+ case 0x0012:
+ PLLPDiv = val & 0xBFFF;
+ return;
case 0x0014:
// shouldn't be instant either?
val &= 0x7FFF;
val |= ((val & 0x0002) << 14);
PLLCnt = val;
return;
+ case 0x0016:
+ ClocksCnt = val;
+ printf("ClocksCnt=%04X\n", val);
+ return;
case 0x0018:
// TODO: this shouldn't be instant, but uh
val &= 0x003F;
val |= ((val & 0x0001) << 14);
StandbyCnt = val;
+ printf("CAM%d STBCNT=%04X (%04X)\n", Num, StandbyCnt, val);
+ return;
+ case 0x001A:
+ MiscCnt = val & 0x0B7B;
+ printf("CAM%d MISCCNT=%04X (%04X)\n", Num, MiscCnt, val);
+ return;
+ }
+
+ if(Num==1)printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
+}
+
+
+u8 DSi_Camera::Read8(u32 addr)
+{
+ //
+
+ printf("unknown DSi cam read8 %08X\n", addr);
+ return 0;
+}
+
+u16 DSi_Camera::Read16(u32 addr)
+{printf("CAM READ %08X %08X\n", addr, NDS::GetPC(0));
+ switch (addr)
+ {
+ case 0x04004200: return ModuleCnt;
+ case 0x04004202: return Cnt;
+ }
+
+ printf("unknown DSi cam read16 %08X\n", addr);
+ return 0;
+}
+u32 dorp = 0;
+u32 DSi_Camera::Read32(u32 addr)
+{
+ switch (addr)
+ {
+ case 0x04004204:
+ {
+ return 0xFC00801F;
+ 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;
+ Cnt &= ~(1<<4);
+ }
+ return ret;
+ }
+ }
+
+ printf("unknown DSi cam read32 %08X\n", addr);
+ return 0;
+}
+
+void DSi_Camera::Write8(u32 addr, u8 val)
+{
+ //
+
+ printf("unknown DSi cam write8 %08X %02X\n", addr, val);
+}
+
+void DSi_Camera::Write16(u32 addr, u16 val)
+{printf("CAM WRITE %08X %04X %08X\n", addr, val, NDS::GetPC(0));
+ switch (addr)
+ {
+ case 0x04004200:
+ {
+ u16 oldcnt = ModuleCnt;
+ ModuleCnt = val;
+
+ if ((ModuleCnt & (1<<1)) && !(oldcnt & (1<<1)))
+ {
+ // reset shit to zero
+ // CHECKME
+
+ Cnt = 0;
+ }
+
+ if ((ModuleCnt & (1<<5)) && !(oldcnt & (1<<5)))
+ {
+ // TODO: reset I2C??
+ }
+ }
+ return;
+
+ case 0x04004202:
+ {
+ // checkme
+ u16 oldmask;
+ if (Cnt & 0x8000)
+ {
+ val &= 0x8F20;
+ oldmask = 0x601F;
+ }
+ else
+ {
+ val &= 0xEF2F;
+ oldmask = 0x0010;
+ }
+
+ Cnt = (Cnt & oldmask) | (val & ~0x0020);
+ if (val & (1<<5)) Cnt &= ~(1<<4);
+
+ if ((val & (1<<15)) && !(Cnt & (1<<15)))
+ {
+ // start transfer
+ //DSi::CheckNDMAs(0, 0x0B);
+ }
+ }
return;
}
- //printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
+ printf("unknown DSi cam write16 %08X %04X\n", addr, val);
+}
+
+void DSi_Camera::Write32(u32 addr, u32 val)
+{
+ //
+
+ printf("unknown DSi cam write32 %08X %08X\n", addr, val);
}