diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..bb833598 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: staplebutter diff --git a/.gitignore b/.gitignore index f63e2394..bd1d4857 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ obj melon_grc.c melon_grc.h cmake-build +cmake-build-debug +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebb69fe..048dd44a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,11 @@ endif() if(ENABLE_LTO) add_compile_options(-O3 -flto) + set(CMAKE_AR "gcc-ar") + set(CMAKE_C_ARCHIVE_CREATE " qcs ") + set(CMAKE_C_ARCHIVE_FINISH true) + set(CMAKE_CXX_ARCHIVE_CREATE " qcs ") + set(CMAKE_CXX_ARCHIVE_FINISH true) endif() add_compile_options(-fno-pic) diff --git a/README.md b/README.md index 293af515..cf0afed2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

melonDS

- +

diff --git a/melon.rc b/melon.rc index c1d773d3..e2d8dc28 100644 --- a/melon.rc +++ b/melon.rc @@ -6,8 +6,8 @@ //include version information in .exe, modify these values to match your needs 1 VERSIONINFO -FILEVERSION 0,8,1,0 -PRODUCTVERSION 0,8,1,0 +FILEVERSION 0,8,3,0 +PRODUCTVERSION 0,8,3,0 FILETYPE VFT_APP { BLOCK "StringFileInfo" @@ -15,14 +15,14 @@ FILETYPE VFT_APP BLOCK "040904E4" { VALUE "CompanyName", "Melon Factory of Kuribo64" - VALUE "FileVersion", "0.8.1" + VALUE "FileVersion", "0.8.3" VALUE "FileDescription", "DS emulator, sorta. also 1st quality melon." VALUE "InternalName", "SDnolem" VALUE "LegalCopyright", "2016-2019 Arisotura & co." VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "zafkflzdasd.exe" VALUE "ProductName", "melonDS" - VALUE "ProductVersion", "0.8.1" + VALUE "ProductVersion", "0.8.3" } } BLOCK "VarFileInfo" diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index f23d6b2e..d60d8f87 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -106,12 +106,12 @@ namespace ARMInterpreter x = ROR(x, (s&0x1F)); #define LSL_REG_S(x, s) \ - if (s > 31) { cpu->SetC(x & (1<<0)); x = 0; } \ - else if (s > 0) { cpu->SetC(x & (1<<(32-s))); x <<= s; } + if (s > 31) { cpu->SetC((s>32) ? 0 : (x & (1<<0))); x = 0; } \ + else if (s > 0) { cpu->SetC(x & (1<<(32-s))); x <<= s; } #define LSR_REG_S(x, s) \ - if (s > 31) { cpu->SetC(x & (1<<31)); x = 0; } \ - else if (s > 0) { cpu->SetC(x & (1<<(s-1))); x >>= s; } + if (s > 31) { cpu->SetC((s>32) ? 0 : (x & (1<<31))); x = 0; } \ + else if (s > 0) { cpu->SetC(x & (1<<(s-1))); x >>= s; } #define ASR_REG_S(x, s) \ if (s > 31) { cpu->SetC(x & (1<<31)); x = ((s32)x) >> 31; } \ @@ -134,7 +134,7 @@ namespace ARMInterpreter #define A_CALC_OP2_REG_SHIFT_REG(shiftop) \ u32 b = cpu->R[cpu->CurInstr&0xF]; \ if ((cpu->CurInstr&0xF)==15) b += 4; \ - shiftop(b, cpu->R[(cpu->CurInstr>>8)&0xF]); + shiftop(b, (cpu->R[(cpu->CurInstr>>8)&0xF] & 0xFF)); #define A_IMPLEMENT_ALU_OP(x,s) \ diff --git a/src/GPU.cpp b/src/GPU.cpp index 0eb321c0..13f2727f 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -284,6 +284,7 @@ void SetDisplaySettings(bool accel) if (Framebuffer[1][0]) delete[] Framebuffer[1][0]; if (Framebuffer[0][1]) delete[] Framebuffer[0][1]; if (Framebuffer[1][1]) delete[] Framebuffer[1][1]; + Framebuffer[0][0] = new u32[fbsize]; Framebuffer[1][0] = new u32[fbsize]; Framebuffer[0][1] = new u32[fbsize]; diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index e0da772b..288ee581 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -246,6 +246,11 @@ u8 GPU2D::Read8(u32 addr) case 0x049: return WinCnt[1]; case 0x04A: return WinCnt[2]; case 0x04B: return WinCnt[3]; + + // there are games accidentally trying to read those + // those are write-only + case 0x04C: + case 0x04D: return 0; } printf("unknown GPU read8 %08X\n", addr); @@ -299,10 +304,22 @@ void GPU2D::Write8(u32 addr, u8 val) switch (addr & 0x00000FFF) { - case 0x000: DispCnt = (DispCnt & 0xFFFFFF00) | val; return; - case 0x001: DispCnt = (DispCnt & 0xFFFF00FF) | (val << 8); return; - case 0x002: DispCnt = (DispCnt & 0xFF00FFFF) | (val << 16); return; - case 0x003: DispCnt = (DispCnt & 0x00FFFFFF) | (val << 24); return; + case 0x000: + DispCnt = (DispCnt & 0xFFFFFF00) | val; + if (Num) DispCnt &= 0xC0B1FFF7; + return; + case 0x001: + DispCnt = (DispCnt & 0xFFFF00FF) | (val << 8); + if (Num) DispCnt &= 0xC0B1FFF7; + return; + case 0x002: + DispCnt = (DispCnt & 0xFF00FFFF) | (val << 16); + if (Num) DispCnt &= 0xC0B1FFF7; + return; + case 0x003: + DispCnt = (DispCnt & 0x00FFFFFF) | (val << 24); + if (Num) DispCnt &= 0xC0B1FFF7; + return; case 0x008: BGCnt[0] = (BGCnt[0] & 0xFF00) | val; return; case 0x009: BGCnt[0] = (BGCnt[0] & 0x00FF) | (val << 8); return; @@ -381,8 +398,14 @@ void GPU2D::Write16(u32 addr, u16 val) switch (addr & 0x00000FFF) { - case 0x000: DispCnt = (DispCnt & 0xFFFF0000) | val; return; - case 0x002: DispCnt = (DispCnt & 0x0000FFFF) | (val << 16); return; + case 0x000: + DispCnt = (DispCnt & 0xFFFF0000) | val; + if (Num) DispCnt &= 0xC0B1FFF7; + return; + case 0x002: + DispCnt = (DispCnt & 0x0000FFFF) | (val << 16); + if (Num) DispCnt &= 0xC0B1FFF7; + return; case 0x008: BGCnt[0] = val; return; case 0x00A: BGCnt[1] = val; return; @@ -514,6 +537,7 @@ void GPU2D::Write32(u32 addr, u32 val) { case 0x000: DispCnt = val; + if (Num) DispCnt &= 0xC0B1FFF7; return; case 0x028: @@ -704,10 +728,6 @@ void GPU2D::DrawScanline(u32 line) // oddly that's not the case for GPU A if (Num && !Enabled) forceblank = true; - // forced blank - // (checkme: are there still things that can run under this mode? likely not) - if (DispCnt & (1<<7)) forceblank = true; - if (forceblank) { for (int i = 0; i < 256; i++) @@ -748,8 +768,10 @@ void GPU2D::DrawScanline(u32 line) case 1: // regular display { - for (int i = 0; i < stride; i+=2) + int i = 0; + for (; i < (stride & ~1); i+=2) *(u64*)&dst[i] = *(u64*)&BGOBJLine[i]; + if (stride & 1) dst[i] = BGOBJLine[i]; } break; @@ -1313,12 +1335,6 @@ void GPU2D::DrawScanlineBGMode(u32 line) void GPU2D::DrawScanlineBGMode6(u32 line) { - if (Num) - { - printf("GPU2D: MODE6 ON SUB GPU???\n"); - return; - } - for (int i = 3; i >= 0; i--) { if ((BGCnt[2] & 0x3) == i) @@ -1332,7 +1348,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line) { if (DispCnt & 0x0100) { - if (DispCnt & 0x8) + if ((!Num) && (DispCnt & 0x8)) DrawBG_3D(); } } @@ -1341,8 +1357,45 @@ void GPU2D::DrawScanlineBGMode6(u32 line) } } +void GPU2D::DrawScanlineBGMode7(u32 line) +{ + // mode 7 only has text-mode BG0 and BG1 + + for (int i = 3; i >= 0; i--) + { + if ((BGCnt[1] & 0x3) == i) + { + if (DispCnt & 0x0200) + { + DrawBG_Text(line, 1); + } + } + if ((BGCnt[0] & 0x3) == i) + { + if (DispCnt & 0x0100) + { + if ((!Num) && (DispCnt & 0x8)) + DrawBG_3D(); + else + DrawBG_Text(line, 0); + } + } + if ((DispCnt & 0x1000) && NumSprites) + InterleaveSprites(0x8000 | (i<<16)); + } +} + void GPU2D::DrawScanline_BGOBJ(u32 line) { + // forced blank disables BG/OBJ compositing + if (DispCnt & (1<<7)) + { + for (int i = 0; i < 256; i++) + BGOBJLine[i] = 0xFF3F3F3F; + + return; + } + u64 backdrop; if (Num) backdrop = *(u16*)&GPU::Palette[0x400]; else backdrop = *(u16*)&GPU::Palette[0]; @@ -1364,7 +1417,6 @@ void GPU2D::DrawScanline_BGOBJ(u32 line) else memset(WindowMask, 0xFF, 256); - // TODO: what happens in mode 7? mode 6 on the sub engine? switch (DispCnt & 0x7) { case 0: DrawScanlineBGMode<0>(line); break; @@ -1374,6 +1426,7 @@ void GPU2D::DrawScanline_BGOBJ(u32 line) case 4: DrawScanlineBGMode<4>(line); break; case 5: DrawScanlineBGMode<5>(line); break; case 6: DrawScanlineBGMode6(line); break; + case 7: DrawScanlineBGMode7(line); break; } // color special effects @@ -2035,14 +2088,19 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2 u32 tilesetaddr, tilemapaddr; u16* pal; + // large BG sizes: + // 0: 512x1024 + // 1: 1024x512 + // 2: 512x256 + // 3: 512x512 u32 xmask, ymask; u32 yshift; switch (bgcnt & 0xC000) { case 0x0000: xmask = 0x1FFFF; ymask = 0x3FFFF; yshift = 9; break; case 0x4000: xmask = 0x3FFFF; ymask = 0x1FFFF; yshift = 10; break; - case 0x8000: // TODO (most likely the second size bit is just ignored) - case 0xC000: printf("bad BG size for large BG: %04X\n", bgcnt); return; + case 0x8000: xmask = 0x1FFFF; ymask = 0x0FFFF; yshift = 9; break; + case 0xC000: xmask = 0x1FFFF; ymask = 0x1FFFF; yshift = 9; break; } u32 ofxmask, ofymask; @@ -2134,21 +2192,19 @@ void GPU2D::DrawSprites(u32 line) const s32 spritewidth[16] = { - 8, 16, 8, 0, - 16, 32, 8, 0, - 32, 32, 16, 0, - 64, 64, 32, 0 + 8, 16, 8, 8, + 16, 32, 8, 8, + 32, 32, 16, 8, + 64, 64, 32, 8 }; const s32 spriteheight[16] = { - 8, 8, 16, 0, - 16, 8, 32, 0, - 32, 16, 32, 0, - 64, 32, 64, 0 + 8, 8, 16, 8, + 16, 8, 32, 8, + 32, 16, 32, 8, + 64, 32, 64, 8 }; - u32 nsprites = 0; - for (int bgnum = 0x0C00; bgnum >= 0x0000; bgnum -= 0x0400) { for (int sprnum = 127; sprnum >= 0; sprnum--) @@ -2287,8 +2343,10 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 { if (DispCnt & 0x20) { - // TODO ("reserved") - printf("bad reserved mode\n"); + // 'reserved' + // draws nothing + + return; } else { @@ -2520,8 +2578,10 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos) { if (DispCnt & 0x20) { - // TODO ("reserved") - printf("bad reserved mode\n"); + // 'reserved' + // draws nothing + + return; } else { diff --git a/src/GPU2D.h b/src/GPU2D.h index b9a24224..6ad97830 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -135,6 +135,7 @@ private: template void DrawScanlineBGMode(u32 line); void DrawScanlineBGMode6(u32 line); + void DrawScanlineBGMode7(u32 line); void DrawScanline_BGOBJ(u32 line); static void DrawPixel_Normal(u32* dst, u16 color, u32 flag); diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 0d866904..d616e1c7 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -342,36 +342,11 @@ bool Init() SetupDefaultTexParams(FramebufferTex[7]); // downscale framebuffer for antialiased mode - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[2]); SetupDefaultTexParams(FramebufferTex[2]); // downscale framebuffer for display capture (always 256x192) - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[3]); SetupDefaultTexParams(FramebufferTex[3]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[3], 0); - - GLenum fbassign[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; - - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[0], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[4], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[5], 0); - glDrawBuffers(2, fbassign); - - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[1]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[1], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[4], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[5], 0); - glDrawBuffers(2, fbassign); - - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[2]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[2], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[6], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[7], 0); - glDrawBuffers(2, fbassign); - - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); glEnable(GL_BLEND); glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX); @@ -475,11 +450,36 @@ void UpdateDisplaySettings() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8UI, ScreenW, ScreenH, 0, GL_RGB_INTEGER, GL_UNSIGNED_BYTE, NULL); } + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[3]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[3], 0); + + GLenum fbassign[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; + + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[0], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[4], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[5], 0); + glDrawBuffers(2, fbassign); + + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[1]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[1], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[4], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[5], 0); + glDrawBuffers(2, fbassign); + + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[2]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[2], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[6], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[7], 0); + glDrawBuffers(2, fbassign); + + glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); + glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelbufferID); glBufferData(GL_PIXEL_PACK_BUFFER, 256*192*4, NULL, GL_DYNAMIC_READ); //glLineWidth(scale); - glLineWidth(1.5); + //glLineWidth(1.5); } @@ -743,6 +743,7 @@ void RenderSceneChunk(int y, int h) // pass 1: opaque pixels UseRenderShader(flags); + glLineWidth(1.0); glColorMaski(1, GL_TRUE, GL_TRUE, fogenable, GL_FALSE); @@ -774,6 +775,7 @@ void RenderSceneChunk(int y, int h) if (RenderDispCnt & (1<<5)) { UseRenderShader(flags | RenderFlag_Edge); + glLineWidth(1.5); glColorMaski(0, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMaski(1, GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE); @@ -806,6 +808,7 @@ void RenderSceneChunk(int y, int h) glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); UseRenderShader(flags | RenderFlag_Trans); + glLineWidth(1.0); if (NumOpaqueFinalPolys > -1) { diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index afe75186..2545ee0d 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -656,7 +656,7 @@ void main() int zshift = (attr >> 16) & 0x1F; vec4 fpos; - fpos.xy = (((vec2(vPosition.xy) + 0.5) * 2.0) / uScreenSize) - 1.0; + fpos.xy = (((vec2(vPosition.xy) ) * 2.0) / uScreenSize) - 1.0; fpos.z = (float(vPosition.z << zshift) / 8388608.0) - 1.0; fpos.w = float(vPosition.w) / 65536.0f; fpos.xyz *= fpos.w; @@ -679,7 +679,7 @@ void main() int zshift = (attr >> 16) & 0x1F; vec4 fpos; - fpos.xy = (((vec2(vPosition.xy) + 0.5) * 2.0) / uScreenSize) - 1.0; + fpos.xy = (((vec2(vPosition.xy) ) * 2.0) / uScreenSize) - 1.0; fZ = float(vPosition.z << zshift) / 16777216.0; fpos.w = float(vPosition.w) / 65536.0f; fpos.xy *= fpos.w; diff --git a/src/NDS.cpp b/src/NDS.cpp index 6933e956..18e2ae1e 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -72,7 +72,7 @@ u64 FrameStartTimestamp; int CurCPU; -const s32 kMaxIterationCycles = 16; +const s32 kMaxIterationCycles = 64; u32 ARM9ClockShift; @@ -1516,7 +1516,7 @@ void DivDone(u32 param) if (den == 0) { DivQuotient[0] = (num<0) ? 1:-1; - DivQuotient[1] = (num<0) ? -1:1; + DivQuotient[1] = (num<0) ? -1:0; *(s64*)&DivRemainder[0] = num; } else if (num == -0x80000000 && den == -1) @@ -1648,7 +1648,8 @@ void debug(u32 param) // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); /*FILE* - shit = fopen("debug/card.bin", "wb"); + shit = fopen("debug/party.bin", "wb"); + fwrite(ARM9->ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { u32 val = ARM7Read32(i); @@ -2936,6 +2937,11 @@ void ARM9IOWrite32(u32 addr, u32 val) { switch (addr) { + case 0x04000004: + GPU::SetDispStat(0, val & 0xFFFF); + GPU::SetVCount(val >> 16); + return; + case 0x04000060: GPU3D::Write32(addr, val); return; case 0x04000064: case 0x04000068: GPU::GPU2D_A->Write32(addr, val); return; @@ -3500,6 +3506,11 @@ void ARM7IOWrite32(u32 addr, u32 val) { switch (addr) { + case 0x04000004: + GPU::SetDispStat(1, val & 0xFFFF); + GPU::SetVCount(val >> 16); + return; + case 0x040000B0: DMAs[4]->SrcAddr = val; return; case 0x040000B4: DMAs[4]->DstAddr = val; return; case 0x040000B8: DMAs[4]->WriteCnt(val); return; @@ -3583,6 +3594,8 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x04000210: IE[1] = val; UpdateIRQ(1); return; case 0x04000214: IF[1] &= ~val; UpdateIRQ(1); return; + case 0x04000304: PowerControl7 = val & 0xFFFF; return; + case 0x04000308: if (ARM7BIOSProt == 0) ARM7BIOSProt = val & 0xFFFE; diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index fbc100cd..a08bcdf1 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -112,8 +112,6 @@ func(GLGETUNIFORMLOCATION, glGetUniformLocation); \ func(GLGETUNIFORMBLOCKINDEX, glGetUniformBlockIndex); \ \ - func(GLBINDIMAGETEXTURE, glBindImageTexture); \ - \ func(GLDRAWBUFFERS, glDrawBuffers); \ \ func(GLBLENDFUNCSEPARATE, glBlendFuncSeparate); \ diff --git a/src/SPU.cpp b/src/SPU.cpp index d31a3717..8e295c30 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -28,7 +28,6 @@ // * channel hold // * 'length less than 4' glitch - namespace SPU { @@ -66,8 +65,8 @@ const u32 kSamplesPerRun = 1; const u32 OutputBufferSize = 2*1024; s16 OutputBuffer[2 * OutputBufferSize]; -u32 OutputReadOffset; -u32 OutputWriteOffset; +volatile u32 OutputReadOffset; +volatile u32 OutputWriteOffset; u16 Cnt; @@ -100,9 +99,7 @@ void DeInit() void Reset() { - memset(OutputBuffer, 0, 2*OutputBufferSize*2); - OutputReadOffset = 0; - OutputWriteOffset = OutputBufferSize; + InitOutput(); Cnt = 0; MasterVolume = 0; @@ -632,7 +629,7 @@ void Mix(u32 samples) else if (val > 0x7FFF) val = 0x7FFF; Capture[0]->Run(val); - if (!((Capture[0]->Cnt & (1<<7)))) break; + if (!(Capture[0]->Cnt & (1<<7))) break; } } @@ -647,7 +644,7 @@ void Mix(u32 samples) else if (val > 0x7FFF) val = 0x7FFF; Capture[1]->Run(val); - if (!((Capture[1]->Cnt & (1<<7)))) break; + if (!(Capture[1]->Cnt & (1<<7))) break; } } @@ -737,12 +734,76 @@ void Mix(u32 samples) OutputBuffer[OutputWriteOffset + 1] = r >> 1; OutputWriteOffset += 2; OutputWriteOffset &= ((2*OutputBufferSize)-1); + if (OutputWriteOffset == OutputReadOffset) + { + //printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1); + // advance the read position too, to avoid losing the entire FIFO + OutputReadOffset += 2; + OutputReadOffset &= ((2*OutputBufferSize)-1); + } } NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun); } +void TrimOutput() +{ + const int halflimit = (OutputBufferSize / 2); + + int readpos = OutputWriteOffset - (halflimit*2); + if (readpos < 0) readpos += (OutputBufferSize*2); + + OutputReadOffset = readpos; +} + +void DrainOutput() +{ + OutputReadOffset = 0; + OutputWriteOffset = 0; +} + +void InitOutput() +{ + memset(OutputBuffer, 0, 2*OutputBufferSize*2); + OutputReadOffset = 0; + OutputWriteOffset = OutputBufferSize; +} + +int GetOutputSize() +{ + int ret; + if (OutputWriteOffset >= OutputReadOffset) + ret = OutputWriteOffset - OutputReadOffset; + else + ret = (OutputBufferSize*2) - OutputReadOffset + OutputWriteOffset; + + ret >>= 1; + return ret; +} + +void Sync(bool wait) +{ + // sync to audio output in case the core is running too fast + // * wait=true: wait until enough audio data has been played + // * wait=false: merely skip some audio data to avoid a FIFO overflow + + const int halflimit = (OutputBufferSize / 2); + + if (wait) + { + // TODO: less CPU-intensive wait? + while (GetOutputSize() > halflimit); + } + else if (GetOutputSize() > halflimit) + { + int readpos = OutputWriteOffset - (halflimit*2); + if (readpos < 0) readpos += (OutputBufferSize*2); + + OutputReadOffset = readpos; + } +} + int ReadOutput(s16* data, int samples) { if (OutputReadOffset == OutputWriteOffset) @@ -910,8 +971,8 @@ void Write16(u32 addr, u16 val) return; case 0xA: chan->SetLoopPos(val); return; - case 0xC: chan->SetLength((chan->Length & 0xFFFF0000) | val); return; - case 0xE: chan->SetLength((chan->Length & 0x0000FFFF) | (val << 16)); return; + case 0xC: chan->SetLength(((chan->Length >> 2) & 0xFFFF0000) | val); return; + case 0xE: chan->SetLength(((chan->Length >> 2) & 0x0000FFFF) | (val << 16)); return; } } else diff --git a/src/SPU.h b/src/SPU.h index 3cd5df34..53a8e0a3 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -35,6 +35,11 @@ void SetBias(u16 bias); void Mix(u32 samples); +void TrimOutput(); +void DrainOutput(); +void InitOutput(); +int GetOutputSize(); +void Sync(bool wait); int ReadOutput(s16* data, int samples); u8 Read8(u32 addr); diff --git a/src/libui_sdl/CMakeLists.txt b/src/libui_sdl/CMakeLists.txt index 63e9f484..64206bf2 100644 --- a/src/libui_sdl/CMakeLists.txt +++ b/src/libui_sdl/CMakeLists.txt @@ -14,6 +14,10 @@ SET(SOURCES_LIBUI OSD.cpp ) +if (WIN32) + set(CMAKE_RC_COMPILE_OBJECT " -i -o ") +endif() + option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) set(BUILD_SHARED_LIBS OFF) add_subdirectory(libui) @@ -39,11 +43,11 @@ if (UNIX) ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER}) add_custom_command(OUTPUT melon_grc.c - COMMAND glib-compile-resources --sourcedir="${CMAKE_SOURCE_DIR}" - --target="${CMAKE_CURRENT_BINARY_DIR}/melon_grc.c" + COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR} + --target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.c --generate-source "${CMAKE_SOURCE_DIR}/melon_grc.xml" - COMMAND glib-compile-resources --sourcedir="${CMAKE_SOURCE_DIR}" - --target="${CMAKE_CURRENT_BINARY_DIR}/melon_grc.h" + COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR} + --target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.h --generate-header "${CMAKE_SOURCE_DIR}/melon_grc.xml") if (CMAKE_SYSTEM_NAME STREQUAL "Linux") diff --git a/src/libui_sdl/DlgInputConfig.cpp b/src/libui_sdl/DlgInputConfig.cpp index 88822753..a80e2ec7 100644 --- a/src/libui_sdl/DlgInputConfig.cpp +++ b/src/libui_sdl/DlgInputConfig.cpp @@ -125,21 +125,30 @@ void JoyMappingName(int id, char* str) return; } - if (id & 0x100) - { - int hatnum = ((id >> 4) & 0xF) + 1; + bool hasbtn = ((id & 0xFFFF) != 0xFFFF); - switch (id & 0xF) + if (hasbtn) + { + if (id & 0x100) { - case 0x1: sprintf(str, "Hat %d up", hatnum); break; - case 0x2: sprintf(str, "Hat %d right", hatnum); break; - case 0x4: sprintf(str, "Hat %d down", hatnum); break; - case 0x8: sprintf(str, "Hat %d left", hatnum); break; + int hatnum = ((id >> 4) & 0xF) + 1; + + switch (id & 0xF) + { + case 0x1: sprintf(str, "Hat %d up", hatnum); break; + case 0x2: sprintf(str, "Hat %d right", hatnum); break; + case 0x4: sprintf(str, "Hat %d down", hatnum); break; + case 0x8: sprintf(str, "Hat %d left", hatnum); break; + } + } + else + { + sprintf(str, "Button %d", (id & 0xFFFF) + 1); } } else { - sprintf(str, "Button %d", (id & 0xFFFF) + 1); + strcpy(str, ""); } if (id & 0x10000) @@ -151,9 +160,9 @@ void JoyMappingName(int id, char* str) switch ((id >> 20) & 0xF) { - case 0: sprintf(str, "%s / Axis %d +", tmp, axisnum); break; - case 1: sprintf(str, "%s / Axis %d -", tmp, axisnum); break; - case 2: sprintf(str, "%s / Trigger %d", tmp, axisnum); break; + case 0: sprintf(str, "%s%sAxis %d +", tmp, hasbtn?" / ":"", axisnum); break; + case 1: sprintf(str, "%s%sAxis %d -", tmp, hasbtn?" / ":"", axisnum); break; + case 2: sprintf(str, "%s%sTrigger %d", tmp, hasbtn?" / ":"", axisnum); break; } } } @@ -223,10 +232,12 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) // set key. if (evt->Scancode != 0x1 || evt->Modifiers != 0) // ESC { + int mod = (dlg->type == 0) ? 0 : evt->Modifiers; + if (evt->Scancode == 0xE && evt->Modifiers == 0) // backspace dlg->keymap[dlg->pollid] = -1; else - dlg->keymap[dlg->pollid] = evt->Scancode | (evt->Modifiers << 16); + dlg->keymap[dlg->pollid] = evt->Scancode | (mod << 16); } char keyname[64]; @@ -287,7 +298,7 @@ Uint32 JoyPoll(Uint32 interval, void* param) } int oldmap; - if (dlg->joymap[id] == -1) oldmap = 0; + if (dlg->joymap[id] == -1) oldmap = 0xFFFF; else oldmap = dlg->joymap[id]; int nbuttons = SDL_JoystickNumButtons(joy); diff --git a/src/libui_sdl/DlgVideoSettings.cpp b/src/libui_sdl/DlgVideoSettings.cpp index 7d876e24..ff88ba8e 100644 --- a/src/libui_sdl/DlgVideoSettings.cpp +++ b/src/libui_sdl/DlgVideoSettings.cpp @@ -39,12 +39,14 @@ uiWindow* win; uiRadioButtons* rbRenderer; uiCheckbox* cbGLDisplay; +uiCheckbox* cbVSync; uiCheckbox* cbThreaded3D; uiCombobox* cbResolution; uiCheckbox* cbAntialias; int old_renderer; int old_gldisplay; +int old_vsync; int old_threaded3D; int old_resolution; int old_antialias; @@ -89,6 +91,11 @@ void RevertSettings() { Config::ScreenUseGL = old_gldisplay; } + if (old_vsync != Config::ScreenVSync) + { + Config::ScreenVSync = old_vsync; + //ApplyNewSettings(4); + } if (old_usegl != new_usegl) { apply2 = true; @@ -130,21 +137,33 @@ void OnRendererChanged(uiRadioButtons* rb, void* blarg) UpdateControls(); bool new_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + if (new_usegl) uiControlEnable(uiControl(cbVSync)); + else uiControlDisable(uiControl(cbVSync)); + if (new_usegl != old_usegl) ApplyNewSettings(2); else ApplyNewSettings(3); - + uiControlSetFocus(uiControl(win)); } void OnGLDisplayChanged(uiCheckbox* cb, void* blarg) { Config::ScreenUseGL = uiCheckboxChecked(cb); + if (Config::ScreenUseGL) uiControlEnable(uiControl(cbVSync)); + else uiControlDisable(uiControl(cbVSync)); ApplyNewSettings(2); uiControlSetFocus(uiControl(win)); } +void OnVSyncChanged(uiCheckbox* cb, void* blarg) +{ + Config::ScreenVSync = uiCheckboxChecked(cb); + //ApplyNewSettings(4); +} + void OnThreaded3DChanged(uiCheckbox* cb, void* blarg) { Config::Threaded3D = uiCheckboxChecked(cb); @@ -232,6 +251,10 @@ void Open() cbGLDisplay = uiNewCheckbox("OpenGL display"); uiCheckboxOnToggled(cbGLDisplay, OnGLDisplayChanged, NULL); uiBoxAppend(in_ctrl, uiControl(cbGLDisplay), 0); + + cbVSync = uiNewCheckbox("VSync"); + uiCheckboxOnToggled(cbVSync, OnVSyncChanged, NULL); + uiBoxAppend(in_ctrl, uiControl(cbVSync), 0); } { @@ -300,17 +323,24 @@ void Open() old_renderer = Config::_3DRenderer; old_gldisplay = Config::ScreenUseGL; + old_vsync = Config::ScreenVSync; old_threaded3D = Config::Threaded3D; old_resolution = Config::GL_ScaleFactor; old_antialias = Config::GL_Antialias; uiCheckboxSetChecked(cbGLDisplay, Config::ScreenUseGL); + uiCheckboxSetChecked(cbVSync, Config::ScreenVSync); uiCheckboxSetChecked(cbThreaded3D, Config::Threaded3D); uiComboboxSetSelected(cbResolution, Config::GL_ScaleFactor-1); //uiCheckboxSetChecked(cbAntialias, Config::GL_Antialias); uiRadioButtonsSetSelected(rbRenderer, Config::_3DRenderer); UpdateControls(); + if (Config::ScreenUseGL || Config::_3DRenderer != 0) + uiControlEnable(uiControl(cbVSync)); + else + uiControlDisable(uiControl(cbVSync)); + uiControlShow(uiControl(win)); } diff --git a/src/libui_sdl/PlatformConfig.cpp b/src/libui_sdl/PlatformConfig.cpp index 7f45b591..c8ec19fb 100644 --- a/src/libui_sdl/PlatformConfig.cpp +++ b/src/libui_sdl/PlatformConfig.cpp @@ -43,9 +43,11 @@ int ScreenSizing; int ScreenFilter; int ScreenUseGL; +int ScreenVSync; int ScreenRatio; int LimitFPS; +int AudioSync; int ShowOSD; int DirectBoot; @@ -118,9 +120,11 @@ ConfigEntry PlatformConfigFile[] = {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0}, {"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0}, + {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, {"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0}, - {"LimitFPS", 0, &LimitFPS, 1, NULL, 0}, + {"LimitFPS", 0, &LimitFPS, 0, NULL, 0}, + {"AudioSync", 0, &AudioSync, 1, NULL, 0}, {"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, {"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, diff --git a/src/libui_sdl/PlatformConfig.h b/src/libui_sdl/PlatformConfig.h index 2c59e5d9..842e72ae 100644 --- a/src/libui_sdl/PlatformConfig.h +++ b/src/libui_sdl/PlatformConfig.h @@ -54,9 +54,11 @@ extern int ScreenSizing; extern int ScreenFilter; extern int ScreenUseGL; +extern int ScreenVSync; extern int ScreenRatio; extern int LimitFPS; +extern int AudioSync; extern int ShowOSD; extern int DirectBoot; diff --git a/src/libui_sdl/libui/ui.h b/src/libui_sdl/libui/ui.h index e15c1270..03aef5de 100644 --- a/src/libui_sdl/libui/ui.h +++ b/src/libui_sdl/libui/ui.h @@ -620,6 +620,7 @@ _UI_EXTERN void *uiGLGetProcAddress(const char* proc); _UI_EXTERN int uiGLGetFramebuffer(uiGLContext* ctx); _UI_EXTERN float uiGLGetFramebufferScale(uiGLContext* ctx); _UI_EXTERN void uiGLSwapBuffers(uiGLContext* ctx); +_UI_EXTERN void uiGLSetVSync(int sync); _UI_ENUM(uiModifiers) { diff --git a/src/libui_sdl/libui/unix/gl.c b/src/libui_sdl/libui/unix/gl.c index e1665eea..e15cf4f3 100644 --- a/src/libui_sdl/libui/unix/gl.c +++ b/src/libui_sdl/libui/unix/gl.c @@ -193,6 +193,11 @@ void uiGLSwapBuffers(uiGLContext* ctx) ctx->backbuffer = ctx->backbuffer ? 0 : 1; } +void uiGLSetVSync(int sync) +{ + // TODO +} + void uiGLMakeContextCurrent(uiGLContext* ctx) { if (!ctx) diff --git a/src/libui_sdl/libui/windows/CMakeLists.txt b/src/libui_sdl/libui/windows/CMakeLists.txt index 9d5313ae..24d4ad96 100644 --- a/src/libui_sdl/libui/windows/CMakeLists.txt +++ b/src/libui_sdl/libui/windows/CMakeLists.txt @@ -73,7 +73,7 @@ macro(_handle_static) add_custom_command( TARGET libui POST_BUILD COMMAND - ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.* ${_LIBUI_STATIC_RES} + ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.obj ${_LIBUI_STATIC_RES} COMMENT "Copying libui.res") endmacro() diff --git a/src/libui_sdl/libui/windows/gl.cpp b/src/libui_sdl/libui/windows/gl.cpp index c6217217..07ef19b1 100644 --- a/src/libui_sdl/libui/windows/gl.cpp +++ b/src/libui_sdl/libui/windows/gl.cpp @@ -3,6 +3,7 @@ #include "area.hpp" #include +#include #include struct uiGLContext @@ -159,3 +160,37 @@ float uiGLGetFramebufferScale(uiGLContext* ctx) // TODO return 1; } + +void uiGLSetVSync(int sync) +{ + static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT = NULL; + static bool symloaded = false; + + if (!symloaded) + { + PFNGLGETSTRINGIPROC _glGetStringi = (PFNGLGETSTRINGIPROC)wglGetProcAddress("glGetStringi"); + if (_glGetStringi == NULL) return; + + GLint numext; + glGetIntegerv(GL_NUM_EXTENSIONS, &numext); + + bool hasswapctrl = false; + for (GLint i = 0; i < numext; i++) + { + const char* ext = (const char*)_glGetStringi(GL_EXTENSIONS, i); + if (!stricmp(ext, "WGL_EXT_swap_control")) + { + hasswapctrl = true; + break; + } + } + + if (hasswapctrl) + _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + + symloaded = true; + } + + if (_wglSwapIntervalEXT) + _wglSwapIntervalEXT(sync); +} diff --git a/src/libui_sdl/libui/windows/window.cpp b/src/libui_sdl/libui/windows/window.cpp index 18d11712..a8f7f234 100644 --- a/src/libui_sdl/libui/windows/window.cpp +++ b/src/libui_sdl/libui/windows/window.cpp @@ -95,7 +95,7 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // not a menu if (lParam != 0) break; - if (HIWORD(wParam) != 0) + if (HIWORD(wParam) != 0 || LOWORD(wParam) <= IDCANCEL) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 34e838c0..d3f9024c 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -93,6 +93,7 @@ uiMenuItem* MenuItem_ScreenSizing[4]; uiMenuItem* MenuItem_ScreenFilter; uiMenuItem* MenuItem_LimitFPS; +uiMenuItem* MenuItem_AudioSync; uiMenuItem* MenuItem_ShowOSD; SDL_Thread* EmuThread; @@ -129,6 +130,8 @@ bool GL_ScreenSizeDirty; int GL_3DScale; +bool GL_VSyncStatus; + int ScreenGap = 0; int ScreenLayout = 0; int ScreenSizing = 0; @@ -158,8 +161,13 @@ bool LidStatus; int JoystickID; SDL_Joystick* Joystick; +int AudioFreq; +float AudioSampleFrac; SDL_AudioDeviceID AudioDevice, MicDevice; +SDL_cond* AudioSync; +SDL_mutex* AudioSyncLock; + u32 MicBufferLength = 2048; s16 MicBuffer[2048]; u32 MicBufferReadPos, MicBufferWritePos; @@ -236,6 +244,8 @@ bool GLScreen_InitOSDShader(GLuint* shader) bool GLScreen_Init() { + GL_VSyncStatus = Config::ScreenVSync; + // TODO: consider using epoxy? if (!OpenGL_Init()) return false; @@ -298,6 +308,13 @@ void GLScreen_DeInit() void GLScreen_DrawScreen() { + bool vsync = Config::ScreenVSync && !HotkeyDown(HK_FastForward); + if (vsync != GL_VSyncStatus) + { + GL_VSyncStatus = vsync; + uiGLSetVSync(vsync); + } + float scale = uiGLGetFramebufferScale(GLContext); glBindFramebuffer(GL_FRAMEBUFFER, uiGLGetFramebuffer(GLContext)); @@ -560,26 +577,42 @@ void MicLoadWav(char* name) void AudioCallback(void* data, Uint8* stream, int len) { - // resampling: - // buffer length is 1024 samples - // which is 710 samples at the original sample rate + len /= (sizeof(s16) * 2); - s16 buf_in[710*2]; + // resample incoming audio to match the output sample rate + + float f_len_in = (len * 32823.6328125) / (float)AudioFreq; + f_len_in += AudioSampleFrac; + int len_in = (int)floor(f_len_in); + AudioSampleFrac = f_len_in - len_in; + + s16 buf_in[1024*2]; s16* buf_out = (s16*)stream; - int num_in = SPU::ReadOutput(buf_in, 710); - int num_out = 1024; + int num_in; + int num_out = len; + + SDL_LockMutex(AudioSyncLock); + num_in = SPU::ReadOutput(buf_in, len_in); + SDL_CondSignal(AudioSync); + SDL_UnlockMutex(AudioSyncLock); + + if (num_in < 1) + { + memset(stream, 0, len*sizeof(s16)*2); + return; + } int margin = 6; - if (num_in < 710-margin) + if (num_in < len_in-margin) { int last = num_in-1; if (last < 0) last = 0; - for (int i = num_in; i < 710-margin; i++) + for (int i = num_in; i < len_in-margin; i++) ((u32*)buf_in)[i] = ((u32*)buf_in)[last]; - num_in = 710-margin; + num_in = len_in-margin; } float res_incr = num_in / (float)num_out; @@ -588,12 +621,22 @@ void AudioCallback(void* data, Uint8* stream, int len) int volume = Config::AudioVolume; - for (int i = 0; i < 1024; i++) + for (int i = 0; i < len; i++) { - // TODO: interp!! buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8; buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8; + /*s16 s_l = buf_in[res_pos*2 ]; + s16 s_r = buf_in[res_pos*2+1]; + + float a = res_timer; + float b = 1.0 - a; + s_l = (s_l * a) + (buf_in[(res_pos-1)*2 ] * b); + s_r = (s_r * a) + (buf_in[(res_pos-1)*2+1] * b); + + buf_out[i*2 ] = (s_l * volume) >> 8; + buf_out[i*2+1] = (s_r * volume) >> 8;*/ + res_timer += res_incr; while (res_timer >= 1.0) { @@ -710,26 +753,31 @@ bool JoystickButtonDown(int val) { if (val == -1) return false; - if (val & 0x100) + bool hasbtn = ((val & 0xFFFF) != 0xFFFF); + + if (hasbtn) { - int hatnum = (val >> 4) & 0xF; - int hatdir = val & 0xF; - Uint8 hatval = SDL_JoystickGetHat(Joystick, hatnum); + if (val & 0x100) + { + int hatnum = (val >> 4) & 0xF; + int hatdir = val & 0xF; + Uint8 hatval = SDL_JoystickGetHat(Joystick, hatnum); - bool pressed = false; - if (hatdir == 0x1) pressed = (hatval & SDL_HAT_UP); - else if (hatdir == 0x4) pressed = (hatval & SDL_HAT_DOWN); - else if (hatdir == 0x2) pressed = (hatval & SDL_HAT_RIGHT); - else if (hatdir == 0x8) pressed = (hatval & SDL_HAT_LEFT); + bool pressed = false; + if (hatdir == 0x1) pressed = (hatval & SDL_HAT_UP); + else if (hatdir == 0x4) pressed = (hatval & SDL_HAT_DOWN); + else if (hatdir == 0x2) pressed = (hatval & SDL_HAT_RIGHT); + else if (hatdir == 0x8) pressed = (hatval & SDL_HAT_LEFT); - if (pressed) return true; - } - else - { - int btnnum = val & 0xFFFF; - Uint8 btnval = SDL_JoystickGetButton(Joystick, btnnum); + if (pressed) return true; + } + else + { + int btnnum = val & 0xFFFF; + Uint8 btnval = SDL_JoystickGetButton(Joystick, btnnum); - if (btnval) return true; + if (btnval) return true; + } } if (val & 0x10000) @@ -833,6 +881,7 @@ bool JoyButtonHeld(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat) void UpdateWindowTitle(void* data) { + if (EmuStatus == 0) return; uiWindowSetTitle(MainWindow, (const char*)data); } @@ -875,6 +924,10 @@ int EmuThreadFunc(void* burp) u32 lasttick = starttick; u32 lastmeasuretick = lasttick; u32 fpslimitcount = 0; + u64 perfcount = SDL_GetPerformanceCounter(); + u64 perffreq = SDL_GetPerformanceFrequency(); + float samplesleft = 0; + u32 nsamples = 0; char melontitle[100]; while (EmuRunning != 0) @@ -886,6 +939,7 @@ int EmuThreadFunc(void* burp) Config::LimitFPS = !Config::LimitFPS; uiQueueMain(UpdateFPSLimit, NULL); } + // TODO: similar hotkeys for video/audio sync? if (HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL); if (HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL); @@ -954,25 +1008,46 @@ int EmuThreadFunc(void* burp) } uiAreaQueueRedrawAll(MainDrawArea); - // framerate limiter based off SDL2_gfx - float framerate = (1000.0f * nlines) / (60.0f * 263.0f); + bool fastforward = HotkeyDown(HK_FastForward); - fpslimitcount++; - u32 curtick = SDL_GetTicks(); - u32 delay = curtick - lasttick; - lasttick = curtick; - - bool limitfps = Config::LimitFPS && !HotkeyDown(HK_FastForward); - - u32 wantedtick = starttick + (u32)((float)fpslimitcount * framerate); - if (curtick < wantedtick && limitfps) + if (Config::AudioSync && !fastforward) { - SDL_Delay(wantedtick - curtick); + SDL_LockMutex(AudioSyncLock); + while (SPU::GetOutputSize() > 1024) + { + int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500); + if (ret == SDL_MUTEX_TIMEDOUT) break; + } + SDL_UnlockMutex(AudioSyncLock); } else { - fpslimitcount = 0; - starttick = curtick; + // ensure the audio FIFO doesn't overflow + //SPU::TrimOutput(); + } + + float framerate = (1000.0f * nlines) / (60.0f * 263.0f); + + { + u32 curtick = SDL_GetTicks(); + u32 delay = curtick - lasttick; + + bool limitfps = Config::LimitFPS && !fastforward; + if (limitfps) + { + float wantedtickF = starttick + (framerate * (fpslimitcount+1)); + u32 wantedtick = (u32)ceil(wantedtickF); + if (curtick < wantedtick) SDL_Delay(wantedtick - curtick); + + lasttick = SDL_GetTicks(); + fpslimitcount++; + if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60)) + { + fpslimitcount = 0; + nsamples = 0; + starttick = lasttick; + } + } } nframes++; @@ -1044,6 +1119,12 @@ int EmuThreadFunc(void* burp) return 44203; } +void StopEmuThread() +{ + EmuRunning = 0; + SDL_WaitThread(EmuThread, NULL); +} + void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params) { @@ -1155,13 +1236,13 @@ void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area) { } -bool EventMatchesKey(uiAreaKeyEvent* evt, int val) +bool EventMatchesKey(uiAreaKeyEvent* evt, int val, bool checkmod) { if (val == -1) return false; int key = val & 0xFFFF; int mod = val >> 16; - return evt->Scancode == key && evt->Modifiers == mod; + return evt->Scancode == key && (!checkmod || evt->Modifiers == mod); } int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) @@ -1175,11 +1256,11 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (evt->Up) { for (int i = 0; i < 12; i++) - if (EventMatchesKey(evt, Config::KeyMapping[i])) + if (EventMatchesKey(evt, Config::KeyMapping[i], false)) KeyInputMask |= (1<Repeat) @@ -1202,16 +1283,16 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) } for (int i = 0; i < 12; i++) - if (EventMatchesKey(evt, Config::KeyMapping[i])) + if (EventMatchesKey(evt, Config::KeyMapping[i], false)) KeyInputMask &= ~(1<Scancode == 0x57) // F11 - NDS::debug(0); + //if (evt->Scancode == 0x57) // F11 + // NDS::debug(0); } return 1; @@ -1486,6 +1567,8 @@ void Run() EmuRunning = 1; RunningSomething = true; + SPU::InitOutput(); + AudioSampleFrac = 0; SDL_PauseAudioDevice(AudioDevice, 0); SDL_PauseAudioDevice(MicDevice, 0); @@ -1524,6 +1607,7 @@ void TogglePause(void* blarg) EmuRunning = 2; uiMenuItemSetChecked(MenuItem_Pause, 1); + SPU::DrainOutput(); SDL_PauseAudioDevice(AudioDevice, 1); SDL_PauseAudioDevice(MicDevice, 1); @@ -1535,6 +1619,8 @@ void TogglePause(void* blarg) EmuRunning = 1; uiMenuItemSetChecked(MenuItem_Pause, 0); + SPU::InitOutput(); + AudioSampleFrac = 0; SDL_PauseAudioDevice(AudioDevice, 0); SDL_PauseAudioDevice(MicDevice, 0); @@ -1585,6 +1671,7 @@ void Stop(bool internal) uiAreaQueueRedrawAll(MainDrawArea); + SPU::DrainOutput(); SDL_PauseAudioDevice(AudioDevice, 1); SDL_PauseAudioDevice(MicDevice, 1); @@ -1849,6 +1936,7 @@ int OnCloseWindow(uiWindow* window, void* blarg) while (EmuStatus != 3); CloseAllDialogs(); + StopEmuThread(); uiQuit(); return 1; } @@ -1886,6 +1974,7 @@ void OnCloseByMenu(uiMenuItem* item, uiWindow* window, void* blarg) while (EmuStatus != 3); CloseAllDialogs(); + StopEmuThread(); DestroyMainWindow(); uiQuit(); } @@ -2143,6 +2232,13 @@ void OnSetLimitFPS(uiMenuItem* item, uiWindow* window, void* blarg) else Config::LimitFPS = false; } +void OnSetAudioSync(uiMenuItem* item, uiWindow* window, void* blarg) +{ + int chk = uiMenuItemChecked(item); + if (chk != 0) Config::AudioSync = true; + else Config::AudioSync = false; +} + void OnSetShowOSD(uiMenuItem* item, uiWindow* window, void* blarg) { int chk = uiMenuItemChecked(item); @@ -2206,6 +2302,19 @@ void ApplyNewSettings(int type) GPU3D::InitRenderer(Screen_UseGL); if (Screen_UseGL) uiGLMakeContextCurrent(NULL); } + /*else if (type == 4) // vsync + { + if (Screen_UseGL) + { + uiGLMakeContextCurrent(GLContext); + uiGLSetVSync(Config::ScreenVSync); + uiGLMakeContextCurrent(NULL); + } + else + { + // TODO eventually: VSync for non-GL screen? + } + }*/ EmuRunning = prevstatus; } @@ -2374,11 +2483,16 @@ void CreateMainWindowMenu() MenuItem_ScreenFilter = uiMenuAppendCheckItem(menu, "Screen filtering"); uiMenuItemOnClicked(MenuItem_ScreenFilter, OnSetScreenFiltering, NULL); + MenuItem_ShowOSD = uiMenuAppendCheckItem(menu, "Show OSD"); + uiMenuItemOnClicked(MenuItem_ShowOSD, OnSetShowOSD, NULL); + + uiMenuAppendSeparator(menu); + MenuItem_LimitFPS = uiMenuAppendCheckItem(menu, "Limit framerate"); uiMenuItemOnClicked(MenuItem_LimitFPS, OnSetLimitFPS, NULL); - MenuItem_ShowOSD = uiMenuAppendCheckItem(menu, "Show OSD"); - uiMenuItemOnClicked(MenuItem_ShowOSD, OnSetShowOSD, NULL); + MenuItem_AudioSync = uiMenuAppendCheckItem(menu, "Audio sync"); + uiMenuItemOnClicked(MenuItem_AudioSync, OnSetAudioSync, NULL); } void CreateMainWindow(bool opengl) @@ -2415,6 +2529,7 @@ void CreateMainWindow(bool opengl) if (opengl_good) { uiGLMakeContextCurrent(GLContext); + uiGLSetVSync(Config::ScreenVSync); if (!GLScreen_Init()) opengl_good = false; if (opengl_good) { @@ -2539,7 +2654,6 @@ int main(int argc, char** argv) SDL_Quit(); return 0; } - { FILE* f = Platform::OpenLocalFile("romlist.bin", "rb"); if (f) @@ -2556,6 +2670,13 @@ int main(int argc, char** argv) "You should use the latest version of romlist.bin (provided in melonDS release packages)."); } } + else + { + uiMsgBoxError(NULL, + "romlist.bin not found.", + "Save memory type detection will not work correctly.\n\n" + "You should use the latest version of romlist.bin (provided in melonDS release packages)."); + } } CreateMainWindowMenu(); @@ -2613,22 +2734,29 @@ int main(int argc, char** argv) uiMenuItemSetChecked(MenuItem_ScreenFilter, Config::ScreenFilter==1); uiMenuItemSetChecked(MenuItem_LimitFPS, Config::LimitFPS==1); + uiMenuItemSetChecked(MenuItem_AudioSync, Config::AudioSync==1); uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1); + AudioSync = SDL_CreateCond(); + AudioSyncLock = SDL_CreateMutex(); + + AudioFreq = 48000; // TODO: make configurable? SDL_AudioSpec whatIwant, whatIget; memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); - whatIwant.freq = 47340; + whatIwant.freq = AudioFreq; whatIwant.format = AUDIO_S16LSB; whatIwant.channels = 2; whatIwant.samples = 1024; whatIwant.callback = AudioCallback; - AudioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0); + AudioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!AudioDevice) { printf("Audio init failed: %s\n", SDL_GetError()); } else { + AudioFreq = whatIget.freq; + printf("Audio output frequency: %d Hz\n", AudioFreq); SDL_PauseAudioDevice(AudioDevice, 1); } @@ -2683,13 +2811,13 @@ int main(int argc, char** argv) uiMain(); - EmuRunning = 0; - SDL_WaitThread(EmuThread, NULL); - if (Joystick) SDL_JoystickClose(Joystick); if (AudioDevice) SDL_CloseAudioDevice(AudioDevice); if (MicDevice) SDL_CloseAudioDevice(MicDevice); + SDL_DestroyCond(AudioSync); + SDL_DestroyMutex(AudioSyncLock); + if (MicWavBuffer) delete[] MicWavBuffer; if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]); diff --git a/src/version.h b/src/version.h index bb97c3c4..0b915409 100644 --- a/src/version.h +++ b/src/version.h @@ -19,7 +19,7 @@ #ifndef VERSION_H #define VERSION_H -#define MELONDS_VERSION "0.8.1-DSi" +#define MELONDS_VERSION "0.8.3-DSi" #define MELONDS_URL "http://melonds.kuribo64.net/"