diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 70f58bc9..c7d00d68 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -31,9 +31,14 @@ Camera* Camera1; // 7A / selfie cam u16 ModuleCnt; u16 Cnt; -u8 FrameBuffer[640*480*4]; +/*u8 FrameBuffer[640*480*4]; 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 // on hardware those are likely affected by several factors @@ -66,9 +71,13 @@ void Reset() ModuleCnt = 0; // CHECKME Cnt = 0; - memset(FrameBuffer, 0, 640*480*4); + /*memset(FrameBuffer, 0, 640*480*4); 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); } @@ -80,9 +89,9 @@ void DoSavestate(Savestate* file) file->Var16(&ModuleCnt); file->Var16(&Cnt); - file->VarArray(FrameBuffer, sizeof(FrameBuffer)); + /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); file->Var32(&TransferPos); - file->Var32(&FrameLength); + file->Var32(&FrameLength);*/ Camera0->DoSavestate(file); Camera1->DoSavestate(file); @@ -101,63 +110,60 @@ void IRQ(u32 param) if (activecam) { - RequestFrame(activecam->Num); + activecam->StartTransfer(); 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); + { + 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); } -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 - // generate test pattern - // TODO: get picture from platform (actual camera, video file, whatever source) - for (u32 y = 0; y < 192; y++) + // TODO HERE: + // convert to RGB5 if needed + // crop if needed + // etc + + // data overrun + if (datalen > maxlen) + Cnt |= (1<<4); + + u32 numscan = Cnt & 0x000F; + if (line >= numscan) { - 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 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 + BufferReadPos = 0; // checkme + BufferWritePos = 0; + line = 0; + DSi::CheckNDMAs(0, 0x0B); } 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); + 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 + u32 delay = datalen*4 + 16; + + NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line); } @@ -187,20 +193,16 @@ u32 Read32(u32 addr) { case 0x04004204: { - // TODO - 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) + u32 ret = DataBuffer[BufferReadPos]; + if (Cnt & (1<<15)) { - dorp = 0; - Cnt &= ~(1<<4); + if (BufferReadPos < 1023) + 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) -{printf("ASS CAMERA REG: %08X %04X\n", addr, val); +{ switch (addr) { case 0x04004200: @@ -255,7 +257,12 @@ void Write16(u32 addr, u16 val) } 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))) { @@ -305,8 +312,6 @@ void Camera::DoSavestate(Savestate* file) file->Var16(&MiscCnt); file->Var16(&MCUAddr); - // TODO: MCUData?? - file->VarArray(MCURegs, 0x8000); } @@ -323,10 +328,26 @@ void Camera::Reset() StandbyCnt = 0x4029; // checkme MiscCnt = 0; + MCUAddr = 0; memset(MCURegs, 0, 0x8000); // default state is preview mode (checkme) 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() @@ -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() { } @@ -496,18 +616,6 @@ void Camera::I2C_WriteReg(u16 addr, u16 val) u8 Camera::MCU_Read(u16 addr) { 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]; } @@ -515,7 +623,6 @@ u8 Camera::MCU_Read(u16 addr) void Camera::MCU_Write(u16 addr, u8 val) { addr &= 0x7FFF; - printf("CAM%d MCU WRITE %04X %02X\n", Num, addr, val); switch (addr) { @@ -526,6 +633,9 @@ void Camera::MCU_Write(u16 addr, u8 val) else if (val != 5 && val != 6) printf("CAM%d: atypical SEQ_CMD %04X\n", Num, val); return; + + case 0x2104: // SEQ_STATE, read-only + return; } MCURegs[addr] = val; diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index a8d558f8..554d41f5 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -37,9 +37,8 @@ void Reset(); void DoSavestate(Savestate* file); void IRQ(u32 param); -void RequestFrame(u32 cam); -void Transfer(u32 pos); +void TransferScanline(u32 line); u8 Read8(u32 addr); u16 Read16(u32 addr); @@ -59,6 +58,12 @@ public: void Reset(); bool IsActivated(); + void StartTransfer(); + bool TransferDone(); + + // lengths in words + int TransferScanline(u32* buffer, int maxlen); + void I2C_Start(); u8 I2C_Read(bool last); void I2C_Write(u8 val, bool last); @@ -81,12 +86,15 @@ private: u16 MiscCnt; u16 MCUAddr; - u16* MCUData; - u8 MCURegs[0x8000]; u8 MCU_Read(u16 addr); void MCU_Write(u16 addr, u8 val); + + u16 FrameWidth, FrameHeight; + u16 FrameReadMode, FrameFormat; + int TransferY; + u32 FrameBuffer[640*480]; }; } diff --git a/src/NDS.cpp b/src/NDS.cpp index 6a6d8f82..b6d0171c 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -723,7 +723,7 @@ bool DoSavestate_Scheduler(Savestate* file) DSi_SDHost::FinishTX, DSi_NWifi::MSTimer, DSi_CamModule::IRQ, - DSi_CamModule::Transfer, + DSi_CamModule::TransferScanline, DSi_DSP::DSPCatchUpU32, nullptr