diff --git a/.gitignore b/.gitignore index a99238ff41ba..3f4019915c31 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ Release Windows/x64 Windows/ipch +# For Mac +.DS_Store + # For ppsspp.ini, etc. *.ini @@ -37,13 +40,16 @@ gen libs obj build*/ +/git-version.cpp .pspsh.hist __testoutput.txt __testerror.txt __testfinish.txt +__testfailure.bmp GameLogNotes.txt android/ui_atlas.zim +android/assets/flash0 ppge_atlas.zim.png local.properties diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a1402a5d020..de75f76c41aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,12 +63,12 @@ else() endif() #find_package(Qt5Widgets) -if(USING_GLES2) +if(USING_GLES2 AND NOT IOS) set(OPENGL_LIBRARIES GLESv2) -else() +elseif(NOT IOS) include(FindOpenGL REQUIRED) endif() -if (NOT BLACKBERRY AND NOT ANDROID) +if (NOT BLACKBERRY AND NOT ANDROID AND NOT IOS) include(FindSDL) endif() include(FindThreads) @@ -119,9 +119,8 @@ if(NOT MSVC) add_definitions(-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1) add_definitions(-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64) endif() - - if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + if(IOS) + add_definitions(-DGL_ETC1_RGB8_OES=0) endif() if(BLACKBERRY) @@ -133,6 +132,12 @@ if(NOT MSVC) add_definitions(-msse2) endif() + if(IOS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++") + elseif(APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + endif() if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") else() @@ -415,9 +420,8 @@ elseif(IOS) ios/AppDelegate.h ios/ViewController.mm ios/ViewController.h) - set(CMAKE_EXE_LINKER_FLAGS "-framework Foundation -framework CoreGraphics -framework QuartzCore -framework OpenGLES -framework UIKit") - # No target - # set(TargetBin PPSSPP) + set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework OpenGLES -framework UIKit -framework GLKit") + set(TargetBin PPSSPP) elseif(USING_QT_UI) # Currently unused find_package(Qt4 COMPONENTS QtMultimedia QtOpenGL QtGui QtCore) @@ -589,6 +593,8 @@ endif() add_library(kirk STATIC ext/libkirk/AES.c ext/libkirk/AES.h + ext/libkirk/amctrl.c + ext/libkirk/amctrl.h ext/libkirk/SHA1.c ext/libkirk/SHA1.h ext/libkirk/bn.c @@ -613,6 +619,8 @@ if(ARM) Core/MIPS/ARM/ArmJitCache.h Core/MIPS/ARM/ArmRegCache.cpp Core/MIPS/ARM/ArmRegCache.h + Core/MIPS/ARM/ArmRegCacheFPU.cpp + Core/MIPS/ARM/ArmRegCacheFPU.h ext/disarm.cpp) elseif(X86) set(CoreExtra ${CoreExtra} @@ -679,6 +687,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/FileSystems/ISOFileSystem.h Core/FileSystems/MetaFileSystem.cpp Core/FileSystems/MetaFileSystem.h + Core/Font/PGF.cpp + Core/Font/PGF.h Core/HLE/FunctionWrappers.h Core/HLE/HLE.cpp Core/HLE/HLE.h @@ -764,6 +774,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceUtility.h Core/HLE/sceVaudio.cpp Core/HLE/sceVaudio.h + Core/HLE/scePspNpDrm_user.cpp + Core/HLE/scePspNpDrm_user.h Core/HW/MediaEngine.cpp Core/HW/MediaEngine.h Core/HW/MemoryStick.cpp @@ -803,6 +815,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/PSPLoaders.h Core/PSPMixer.cpp Core/PSPMixer.h + Core/Reporting.cpp + Core/Reporting.h Core/SaveState.cpp Core/SaveState.h Core/System.cpp @@ -811,15 +825,26 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Util/BlockAllocator.h Core/Util/PPGeDraw.cpp Core/Util/PPGeDraw.h - Core/Util/Pool.h Core/Util/ppge_atlas.cpp Core/Util/ppge_atlas.h $ - Globals.h) + Globals.h + git-version.cpp) target_link_libraries(${CoreLibName} Common native kirk cityhash ${GLEW_LIBRARIES} ${OPENGL_LIBRARIES}) setup_target_project(${CoreLibName} Core) +# Generate git-version.cpp at build time. +add_custom_target(GitVersion ALL + DEPENDS something_that_never_exists) +add_custom_command(OUTPUT something_that_never_exists + COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake) + +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cpp + PROPERTIES GENERATED TRUE) +add_dependencies(${CoreLibName} GitVersion) + add_library(GPU OBJECT GPU/GLES/DisplayListInterpreter.cpp GPU/GLES/DisplayListInterpreter.h @@ -950,15 +975,26 @@ set(NativeAssets set(LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT} ${nativeExtraLibs}) if (TargetBin) - add_executable(${TargetBin} ${NativeAppSource}) + if (IOS) + add_executable(${TargetBin} MACOSX_BUNDLE ${NativeAppSource}) + else() + add_executable(${TargetBin} ${NativeAppSource}) + endif() target_link_libraries(${TargetBin} ${LinkCommon}) endif() +# installs file(INSTALL ${NativeAssets} DESTINATION assets) -# code signing +# packaging and code signing if (IOS) - set_target_properties(${NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST PPSSPP-Info.plist XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer: My Name") + set(RSRC_XIB_FILES assets/Icon@2x.png) + SET_SOURCE_FILES_PROPERTIES(${RSRC_XIB_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set(APP_DIR_NAME \${TARGET_BUILD_DIR}/\${FULL_PRODUCT_NAME}) + add_custom_command(TARGET PPSSPP POST_BUILD + COMMAND tar -c -C . --exclude .DS_Store --exclude .git -H `find assets` | tar -x -C ${APP_DIR_NAME} + ) + set_target_properties(${TargetBin} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "../ios/PPSSPP-Info.plist" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer: My Name") endif() #include(CPack) diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp index afaf6b777bc4..e7d76c205c29 100644 --- a/Common/ArmCPUDetect.cpp +++ b/Common/ArmCPUDetect.cpp @@ -155,11 +155,12 @@ void CPUInfo::Detect() #ifdef __SYMBIAN32__ bThumbEE = false; bNEON = false; + bVFPv3 = false; #else bThumbEE = true; bNEON = true; -#endif bVFPv3 = true; +#endif bTLS = true; bVFPv4 = false; bIDIVa = false; diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 0e3e8bdb93fd..1f58aefd02ba 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -83,65 +83,94 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } +void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg) +{ + union {float f; u32 u;} conv; + conv.f = val; + MOVI2R(tempReg, conv.u); + VMOV(dest, tempReg); +} + +void ARMXEmitter::ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) +{ + Operand2 op2; + bool inverse; + if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!inverse) { + AND(rd, rs, op2); + } else { + BIC(rd, rs, op2); + } + } else { + MOVI2R(scratch, val); + AND(rd, rs, scratch); + } +} + +void ARMXEmitter::ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) +{ + Operand2 op2; + if (TryMakeOperand2(val, op2)) { + ORR(rd, rs, op2); + } else { + MOVI2R(scratch, val); + ORR(rd, rs, scratch); + } +} + +void ARMXEmitter::FlushLitPool() +{ + for(std::vector::iterator it = currentLitPool.begin(); it != currentLitPool.end(); ++it) { + LiteralPool item = *it; + // Write the constant to Literal Pool + s32 offset = (s32)code - (s32)item.ldr_address - 8; + Write32(item.val); + // Backpatch the LDR + *(u32*)item.ldr_address |= (offset >= 0) << 23 | abs(offset); + } + // TODO: Save a copy of previous pools in case they are still in range. + currentLitPool.clear(); +} + +void ARMXEmitter::AddNewLit(u32 val) +{ + // TODO: Look up current constants, + // return that value instead of generating a new one. + LiteralPool pool_item; + pool_item.i = currentLitPool.size(); + pool_item.val = val; + pool_item.ldr_address = code; + currentLitPool.push_back(pool_item); +} + void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) { Operand2 op2; bool inverse; - if (!optimize) + + if (cpu_info.bArmV7 && !optimize) { - // Only used in backpatch atm - // Only support ARMv7 right now - if (cpu_info.bArmV7) { - MOVW(reg, val & 0xFFFF); - MOVT(reg, val, true); - } - else - { - // ARMv6 version won't use backpatch for now - // Run again with optimizations - MOVI2R(reg, val); - } - } else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { - if (!inverse) - MOV(reg, op2); - else - MVN(reg, op2); + // For backpatching on ARMv7 + MOVW(reg, val & 0xFFFF); + MOVT(reg, val, true); + } + else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + inverse ? MVN(reg, op2) : MOV(reg, op2); } else { - if (cpu_info.bArmV7) { - // ARMv7 - can use MOVT/MOVW, best choice + if (cpu_info.bArmV7) + { + // Use MOVW+MOVT for ARMv7+ MOVW(reg, val & 0xFFFF); if(val & 0xFFFF0000) MOVT(reg, val, true); } else { - // ARMv6 - fallback sequence. - // TODO: Optimize further. Can for example choose negation etc. - // Literal pools is another way to do this but much more complicated - // so I can't really be bothered for an outdated CPU architecture like ARMv6. - bool first = true; - int shift = 16; - for (int i = 0; i < 4; i++) { - if (val & 0xFF) { - if (first) { - MOV(reg, Operand2((u8)val, (u8)(shift & 0xF))); - first = false; - } else { - ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF))); - } - } - shift -= 4; - val >>= 8; - } + // Use literal pool for ARMv6. + AddNewLit(val); + LDRLIT(reg, 0, 0); // To be backpatched later } } } -// Moves IMM to memory location -void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) -{ - // This moves imm to a memory location - MOVW(R14, val); MOVT(R14, val, true); - MOVW(R12, op); MOVT(R12, op, true); - STR(R12, R14); // R10 is what we want to store -} + void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { MOVI2R(reg, (u32)(func)); BL(reg); @@ -151,6 +180,9 @@ void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; startcode = code; +#ifdef IOS + lastCacheFlushEnd = ptr; +#endif } const u8 *ARMXEmitter::GetCodePtr() const @@ -194,7 +226,8 @@ void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end) #elif defined(BLACKBERRY) msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE); #elif defined(IOS) - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); + if (start != NULL) + sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); #elif !defined(_WIN32) __builtin___clear_cache(start, end); #endif @@ -471,6 +504,7 @@ void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedData void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);} +void ARMXEmitter::LSR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(3, false, dest, src, op2);} void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2) { Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2); @@ -494,10 +528,38 @@ void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) Write4OpMultiply(0xC, destLo, destHi, rn, rm); } +void ARMXEmitter::UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xA, destLo, destHi, rn, rm); +} + +void ARMXEmitter::SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xE, destLo, destHi, rn, rm); +} + +void ARMXEmitter::UBFX(ARMReg dest, ARMReg rn, u8 lsb, u8 width) +{ + Write32(condition | (0x7E0 << 16) | ((width - 1) << 16) | (dest << 12) | (lsb << 7) | (5 << 4) | rn); +} + +void ARMXEmitter::CLZ(ARMReg rd, ARMReg rm) +{ + Write32(condition | (0x16F << 16) | (rd << 12) | (0xF1 << 4) | rm); +} + +void ARMXEmitter::BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width) +{ + u32 msb = (lsb + width - 1); + if (msb > 31) msb = 31; + Write32(condition | (0x7C0 << 16) | (msb << 16) | (rd << 12) | (lsb << 7) | (1 << 4) | rn); +} + void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2) { Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2); } + void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation) { SXTAH(dest, (ARMReg)15, op2, rotation); @@ -508,9 +570,13 @@ void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation) // information Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2); } -void ARMXEmitter::REV (ARMReg dest, ARMReg src ) +void ARMXEmitter::RBIT(ARMReg dest, ARMReg src) +{ + Write32(condition | (0x6F << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); +} +void ARMXEmitter::REV (ARMReg dest, ARMReg src) { - Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); + Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); } void ARMXEmitter::REV16(ARMReg dest, ARMReg src) { @@ -537,12 +603,29 @@ void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2) else Write32(condition | (op << 20) | (3 << 23) | (dest << 16) | (src << 12) | op2.Imm12()); } -void ARMXEmitter::STR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x40, dest, src, op);} -void ARMXEmitter::STRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x44, dest, src, op);} -void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::STR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x40, dest, src, op);} +void ARMXEmitter::STRH (ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x04 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); +} +void ARMXEmitter::STRB (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x44, dest, src, op);} +void ARMXEmitter::STR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | op2.IMMSR()); +} +void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); } +void ARMXEmitter::STRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x00 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | (0xB << 4) | offset); +} +void ARMXEmitter::STRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x64 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); +} void ARMXEmitter::LDREX(ARMReg dest, ARMReg base) { Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F); @@ -567,12 +650,44 @@ void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op) u8 Imm = op.Imm8(); Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); } +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xF << 4) | (Imm & 0x0F)); +} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xD << 4) | (Imm & 0x0F)); +} -void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | op2.IMMSR()); +} +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); } +void ARMXEmitter::LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xB << 4) | offset); +} +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xF << 4) | offset); +} +void ARMXEmitter::LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x65 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); +} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xD << 4) | offset); +} +void ARMXEmitter::LDRLIT (ARMReg dest, u32 offset, bool Add) { Write32(condition | 0x05 << 24 | Add << 23 | 0x1F << 16 | dest << 12 | offset);} + void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList) { Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList); @@ -622,19 +737,22 @@ ARMReg ARMXEmitter::SubBase(ARMReg Reg) } return Reg; } + // NEON Specific void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm) { - _assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VADD(integer)"); + _assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)"); _assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it"); + bool register_quad = Vd >= Q0; + // Gets encoded as a double register Vd = SubBase(Vd); Vn = SubBase(Vn); Vm = SubBase(Vm); Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \ - | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \ + | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \ | ((Vm & 0x10) << 2) | (Vm & 0xF)); } @@ -651,16 +769,22 @@ void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm) Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \ | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \ | ((Vm & 0x10) << 2) | (Vm & 0xF)); - } // VFP Specific -void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, Operand2 op) +void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, u16 offset) { _assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR"); _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR"); - _assert_msg_(DYNA_REC, !(op.Imm12() & 4), "Offset needs to be word aligned"); + _assert_msg_(DYNA_REC, (offset & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough"); + + if (offset & 0xC03) { + ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", offset); + } + + // ERROR_LOG(DYNA_REC, "VLDR: s%i, r%i + %i", Dest - S0, Base, offset); + bool single_reg = Dest < D0; Dest = SubBase(Dest); @@ -668,20 +792,26 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, Operand2 op) if (single_reg) { Write32(NO_COND | (0x1B << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ - | ((Dest & 0x1E) << 11) | (10 << 8) | (op.Imm12() >> 2)); + | ((Dest & 0x1E) << 11) | (10 << 8) | (offset >> 2)); } else { Write32(NO_COND | (0x1B << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ - | ((Dest & 0xF) << 12) | (11 << 8) | (op.Imm12() >> 2)); + | ((Dest & 0xF) << 12) | (11 << 8) | (offset >> 2)); } } -void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, Operand2 op) +void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, u16 offset) { _assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR"); _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR"); - _assert_msg_(DYNA_REC, !(op.Imm12() & 4), "Offset needs to be word aligned"); + _assert_msg_(DYNA_REC, (offset & 0xC03) == 0, "VSTR: Offset needs to be word aligned"); + + if (offset & 0xC03) { + ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", offset); + } + // ERROR_LOG(DYNA_REC, "VSTR: s%i, r%i + %i", Src - S0, Base, offset); + bool single_reg = Src < D0; Src = SubBase(Src); @@ -689,16 +819,16 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, Operand2 op) if (single_reg) { Write32(NO_COND | (0x1B << 23) | ((Src & 0x1) << 22) | (Base << 16) \ - | ((Src & 0x1E) << 11) | (10 << 8) | (op.Imm12() >> 2)); + | ((Src & 0x1E) << 11) | (10 << 8) | (offset >> 2)); } else { Write32(NO_COND | (0x1B << 23) | ((Src & 0x10) << 18) | (Base << 16) \ - | ((Src & 0xF) << 12) | (11 << 8) | (op.Imm12() >> 2)); + | ((Src & 0xF) << 12) | (11 << 8) | (offset >> 2)); } } -void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) +void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -709,15 +839,15 @@ void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) if (single_reg) { Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + | (E << 7) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); + | (E << 7) | (0x2C << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); } } -void ARMXEmitter::VCMP(ARMReg Vd) +void ARMXEmitter::VCMP(ARMReg Vd, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -727,14 +857,19 @@ void ARMXEmitter::VCMP(ARMReg Vd) if (single_reg) { Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6)); + | (E << 7) | (0x29 << 6)); } else { Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6)); + | (E << 7) | (0x2C << 6)); } } + +void ARMXEmitter::VMRS_APSR() { + Write32(0xEEF10A10 | (15 << 12)); +} + void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); @@ -748,7 +883,7 @@ void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 16) \ + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \ | ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \ | (Vm >> 1)); } @@ -779,6 +914,7 @@ void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm) | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } } + // VFP and ASIMD void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) { @@ -795,7 +931,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ - | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 12) | (0x5 << 9) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else @@ -830,7 +966,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ - | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 12) | (0x5 << 9) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else @@ -850,6 +986,112 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) } } } +// VFP and ASIMD +void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VADD"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VADD"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x2 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + if (double_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x2 << 20) \ + | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ + | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } + else + { + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VMUL with Quad Reg without support!"); + } + } +} + +void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VMLA"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VMLA"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VMLA"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vn = SubBase(Vn); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x0 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + _assert_msg_(DYNA_REC, false, "VMLA: Please implement!"); + } +} + +void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm) +{ + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x30 << 16) \ + | ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x30 << 16) \ + | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } +} + +void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm) +{ + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + | ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + | ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + } +} + +void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high) +{ + _assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg"); + _assert_msg_(DYNA_REC, Dest >= D0, "This VMOV doesn't support DEST other than VFP"); + + Dest = SubBase(Dest); + + Write32(NO_COND | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \ + | (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4)); +} void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { @@ -934,4 +1176,22 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) } } +void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags) +{ + bool single_reg = (Dest < D0) && (Source < D0); + int op = ((flags & TO_INT) ? (flags & ROUND_TO_ZERO) : (flags & IS_SIGNED)) ? 1 : 0; + int op2 = ((flags & TO_INT) ? (flags & IS_SIGNED) : 0) ? 1 : 0; + Dest = SubBase(Dest); + Source = SubBase(Source); + + if (single_reg) + { + Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0x1E) << 11) | (op << 7) | (0x29 << 6) | ((Source & 0x1) << 5) | (Source >> 1)); + } else { + Write32(NO_COND | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0xF) << 12) | (1 << 8) | (op << 7) | (0x29 << 6) | ((Source & 0x10) << 1) | (Source & 0xF)); + } +} + } diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 636a9a646b82..feab20eb86cd 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -25,6 +25,7 @@ #if defined(__SYMBIAN32__) || defined(PANDORA) #include #endif +#include #undef _IP #undef R0 @@ -32,6 +33,12 @@ #undef _LR #undef _PC +// VCVT flags +#define TO_FLOAT 0 +#define TO_INT 1 << 0 +#define IS_SIGNED 1 << 1 +#define ROUND_TO_ZERO 1 << 2 + namespace ArmGen { enum ARMReg @@ -327,6 +334,13 @@ struct FixupBranch int type; //0 = B 1 = BL }; +struct LiteralPool +{ + int i; + u8* ldr_address; + u32 val; +}; + typedef const u8* JumpTarget; class ARMXEmitter @@ -336,6 +350,7 @@ class ARMXEmitter u8 *code, *startcode; u8 *lastCacheFlushEnd; u32 condition; + std::vector currentLitPool; void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2); void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList); @@ -373,6 +388,10 @@ class ARMXEmitter void FlushIcacheSection(u8 *start, u8 *end); u8 *GetWritableCodePtr(); + void FlushLitPool(); + void AddNewLit(u32 val); + + CCFlags GetCC() { return CCFlags(condition >> 28); } void SetCC(CCFlags cond = CC_AL); // Special purpose instructions @@ -425,8 +444,10 @@ class ARMXEmitter void LSL (ARMReg dest, ARMReg src, ARMReg op2); void LSLS(ARMReg dest, ARMReg src, Operand2 op2); void LSLS(ARMReg dest, ARMReg src, ARMReg op2); + void LSR (ARMReg dest, ARMReg src, Operand2 op2); void SBC (ARMReg dest, ARMReg src, Operand2 op2); void SBCS(ARMReg dest, ARMReg src, Operand2 op2); + void RBIT(ARMReg dest, ARMReg src); void REV (ARMReg dest, ARMReg src); void REV16 (ARMReg dest, ARMReg src); void RSC (ARMReg dest, ARMReg src, Operand2 op2); @@ -457,26 +478,46 @@ class ARMXEmitter void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); void SMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SXTB(ARMReg dest, ARMReg op2); void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0); void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0); + void BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width); + void UBFX(ARMReg dest, ARMReg op2, u8 lsb, u8 width); + void CLZ(ARMReg rd, ARMReg rm); + // Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...) // Just need to put an underscore here, bit annoying. void _MSR (bool nzcvq, bool g, Operand2 op2); - void _MSR (bool nzcvq, bool g, ARMReg src ); + void _MSR (bool nzcvq, bool g, ARMReg src); void MRS (ARMReg dest); // Memory load/store operations - void LDR (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDR (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRH (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRSH(ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRB (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRSB(ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds to the base register in LDR - void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); - void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0); - void LDRB(ARMReg dest, ARMReg src, Operand2 op2 = 0); - void STR (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add); + void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRLIT(ARMReg dest, u32 offset, bool Add); + + void STR (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void STRH (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void STRB (ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds on to the destination register in STR - void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void STR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add); + void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); - void STRB(ARMReg dest, ARMReg src, Operand2 op2 = 0); void STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); @@ -499,23 +540,37 @@ class ARMXEmitter void VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm); // VFP Only - void VLDR(ARMReg Dest, ARMReg Base, Operand2 op); - void VSTR(ARMReg Src, ARMReg Base, Operand2 op); - void VCMP(ARMReg Vd, ARMReg Vm); + void VLDR(ARMReg Dest, ARMReg Base, u16 offset); + void VSTR(ARMReg Src, ARMReg Base, u16 offset); + void VCMP(ARMReg Vd, ARMReg Vm, bool E); // Compares against zero - void VCMP(ARMReg Vd); + void VCMP(ARMReg Vd, bool E); void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VSQRT(ARMReg Vd, ARMReg Vm); // NEON and VFP void VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VABS(ARMReg Vd, ARMReg Vm); + void VNEG(ARMReg Vd, ARMReg Vm); + void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VMOV(ARMReg Dest, ARMReg Src, bool high); void VMOV(ARMReg Dest, ARMReg Src); + void VCVT(ARMReg Dest, ARMReg Src, int flags); + + void VMRS_APSR(); void QuickCallFunction(ARMReg scratchreg, void *func); - // Utility functions + + // Wrapper around MOVT/MOVW with fallbacks. void MOVI2R(ARMReg reg, u32 val, bool optimize = true); - void ARMABI_MOVI2M(Operand2 op, Operand2 val); + void MOVI2F(ARMReg dest, float val, ARMReg tempReg); + + void ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + void ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + + }; // class ARMXEmitter @@ -552,7 +607,9 @@ class ARMXCodeBlock : public ARMXEmitter // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. void FreeCodeSpace() { +#ifndef __SYMBIAN32__ FreeMemoryPages(region, region_size); +#endif region = NULL; region_size = 0; } diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 095831678aa3..8bf1c8223290 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -34,8 +34,12 @@ #include #include #ifndef __SYMBIAN32__ +#ifdef IOS +#include +#else #include #endif +#endif #include "Common.h" #include "FileUtil.h" @@ -625,7 +629,7 @@ class CChunkFileReader u8 *uncomp_buffer = new u8[header.UncompressedSize]; size_t uncomp_size = header.UncompressedSize; snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size); - if (uncomp_size != header.UncompressedSize) { + if ((int)uncomp_size != header.UncompressedSize) { ERROR_LOG(COMMON,"Size mismatch: file: %i calc: %i", (int)header.UncompressedSize, (int)uncomp_size); } ptr = uncomp_buffer; diff --git a/Common/Common.h b/Common/Common.h index d87443e3f918..16b0fd342f2e 100644 --- a/Common/Common.h +++ b/Common/Common.h @@ -25,6 +25,10 @@ #include #include +#ifdef _MSC_VER +#pragma warning (disable:4100) +#endif + #if defined(ARM) #define _M_ARM32 #endif @@ -77,7 +81,9 @@ class NonCopyable #error needs at least version 1000 of MSC #endif +#ifndef NOMINMAX #define NOMINMAX +#endif // Memory leak checks #define CHECK_HEAP_INTEGRITY() diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 0fadc6ef2a13..24ae9672b118 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -313,19 +313,19 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) if (logPendingWritePos == (u32) -1) return; - size_t Len = strlen(Text); + int Len = (int)strlen(Text); if (Len > LOG_PENDING_MAX) Len = LOG_PENDING_MAX - 16; char ColorAttr[16] = ""; - size_t ColorLen = 0; + int ColorLen = 0; if (bUseColor) { // Not ANSI, since the console doesn't support it, but ANSI-like. snprintf(ColorAttr, 16, "\033%d", Level); // For now, rather than properly support it. _dbg_assert_msg_(COMMON, strlen(ColorAttr) == 2, "Console logging doesn't support > 9 levels."); - ColorLen = strlen(ColorAttr); + ColorLen = (int)strlen(ColorAttr); } EnterCriticalSection(&criticalSection); @@ -333,7 +333,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) u32 prevLogWritePos = logWritePos; if (logWritePos + ColorLen + Len >= LOG_PENDING_MAX) { - for (size_t i = 0; i < ColorLen; ++i) + for (int i = 0; i < ColorLen; ++i) logPending[(logWritePos + i) % LOG_PENDING_MAX] = ColorAttr[i]; logWritePos += ColorLen; if (logWritePos >= LOG_PENDING_MAX) @@ -370,7 +370,7 @@ void ConsoleListener::SendToThread(LogTypes::LOG_LEVELS Level, const char *Text) // Okay, have it go right after the next newline. if (nextNewline != NULL) - logPendingReadPos = nextNewline - logPending + 1; + logPendingReadPos = (u32)(nextNewline - logPending + 1); } // Double check we didn't start quitting. diff --git a/Common/Crypto/aes.h b/Common/Crypto/aes.h index 8d5059e1bebd..c3006a775c89 100644 --- a/Common/Crypto/aes.h +++ b/Common/Crypto/aes.h @@ -89,9 +89,9 @@ int AES_set_encrypt_key(const unsigned char* userKey, const int bits, int AES_set_decrypt_key(const unsigned char* userKey, const int bits, AES_KEY* key); -void AES_encrypt(const unsigned char* in, unsigned char* out, +void AES_encrypt1(const unsigned char* in, unsigned char* out, const AES_KEY* key); -void AES_decrypt(const unsigned char* in, unsigned char* out, +void AES_decrypt1(const unsigned char* in, unsigned char* out, const AES_KEY* key); void AES_ecb_encrypt(const unsigned char* in, unsigned char* out, diff --git a/Common/Crypto/aes_cbc.cpp b/Common/Crypto/aes_cbc.cpp index 780c35a88d77..954878a5bba6 100644 --- a/Common/Crypto/aes_cbc.cpp +++ b/Common/Crypto/aes_cbc.cpp @@ -75,7 +75,7 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, while (len >= AES_BLOCK_SIZE) { for(n=0; n < AES_BLOCK_SIZE; ++n) out[n] = in[n] ^ iv[n]; - AES_encrypt(out, out, key); + AES_encrypt1(out, out, key); iv = out; len -= AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; @@ -86,13 +86,13 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, out[n] = in[n] ^ iv[n]; for(n=len; n < AES_BLOCK_SIZE; ++n) out[n] = iv[n]; - AES_encrypt(out, out, key); + AES_encrypt1(out, out, key); iv = out; } memcpy(ivec,iv,AES_BLOCK_SIZE); } else if (in != out) { while (len >= AES_BLOCK_SIZE) { - AES_decrypt(in, out, key); + AES_decrypt1(in, out, key); for(n=0; n < AES_BLOCK_SIZE; ++n) out[n] ^= iv[n]; iv = in; @@ -101,7 +101,7 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, out += AES_BLOCK_SIZE; } if (len) { - AES_decrypt(in,tmp,key); + AES_decrypt1(in,tmp,key); for(n=0; n < len; ++n) out[n] = tmp[n] ^ iv[n]; iv = in; @@ -110,7 +110,7 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, } else { while (len >= AES_BLOCK_SIZE) { memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(in, out, key); + AES_decrypt1(in, out, key); for(n=0; n < AES_BLOCK_SIZE; ++n) out[n] ^= ivec[n]; memcpy(ivec, tmp, AES_BLOCK_SIZE); @@ -120,7 +120,7 @@ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, } if (len) { memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(tmp, out, key); + AES_decrypt1(tmp, out, key); for(n=0; n < len; ++n) out[n] ^= ivec[n]; for(n=len; n < AES_BLOCK_SIZE; ++n) diff --git a/Common/Crypto/aes_core.cpp b/Common/Crypto/aes_core.cpp index ad54bc6006f8..2bce4429a98f 100644 --- a/Common/Crypto/aes_core.cpp +++ b/Common/Crypto/aes_core.cpp @@ -778,7 +778,7 @@ int AES_set_decrypt_key(const unsigned char *userKey, const int bits, * Encrypt a single block * in and out can overlap */ -void AES_encrypt(const unsigned char *in, unsigned char *out, +void AES_encrypt1(const unsigned char *in, unsigned char *out, const AES_KEY *key) { const u32 *rk; @@ -969,7 +969,7 @@ void AES_encrypt(const unsigned char *in, unsigned char *out, * Decrypt a single block * in and out can overlap */ -void AES_decrypt(const unsigned char *in, unsigned char *out, +void AES_decrypt1(const unsigned char *in, unsigned char *out, const AES_KEY *key) { const u32 *rk; diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index ebbff872653f..956a1ae2391d 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -27,8 +27,8 @@ #endif // Unfortunately this is quite slow. -// #define LOG_MSC_OUTPUTDEBUG true #define LOG_MSC_OUTPUTDEBUG false +// #define LOG_MSC_OUTPUTDEBUG true void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char *file, int line, const char* fmt, ...) @@ -46,30 +46,30 @@ LogManager *LogManager::m_logManager = NULL; LogManager::LogManager() { // create log files - m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log"); - m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); - m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); - m_Log[LogTypes::CPU] = new LogContainer("CPU", "CPU"); - m_Log[LogTypes::LOADER] = new LogContainer("LOAD", "Loader"); - m_Log[LogTypes::IO] = new LogContainer("IO", "IO"); - m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "DiscIO"); - m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); - m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); - m_Log[LogTypes::G3D] = new LogContainer("G3D", "3D Graphics"); - m_Log[LogTypes::DMA] = new LogContainer("DMA", "DMA"); - m_Log[LogTypes::INTC] = new LogContainer("INTC", "Interrupts"); - m_Log[LogTypes::MEMMAP] = new LogContainer("MM", "Memory Map"); - m_Log[LogTypes::SOUND] = new LogContainer("SND", "Sound"); - m_Log[LogTypes::SAS] = new LogContainer("SAS", "Sound Mixer (Sas)"); - m_Log[LogTypes::HLE] = new LogContainer("HLE", "HLE"); - m_Log[LogTypes::TIMER] = new LogContainer("TMR", "Timer"); - m_Log[LogTypes::VIDEO] = new LogContainer("VID", "Video"); - m_Log[LogTypes::DYNA_REC] = new LogContainer("Jit", "JIT compiler"); - m_Log[LogTypes::NETPLAY] = new LogContainer("NET", "Net play"); - m_Log[LogTypes::ME] = new LogContainer("ME", "Media Engine"); + m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log"); + m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); + m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); + m_Log[LogTypes::CPU] = new LogContainer("CPU", "CPU"); + m_Log[LogTypes::LOADER] = new LogContainer("LOAD", "Loader"); + m_Log[LogTypes::IO] = new LogContainer("IO", "IO"); + m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "DiscIO"); + m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); + m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); + m_Log[LogTypes::G3D] = new LogContainer("G3D", "3D Graphics"); + m_Log[LogTypes::DMA] = new LogContainer("DMA", "DMA"); + m_Log[LogTypes::INTC] = new LogContainer("INTC", "Interrupts"); + m_Log[LogTypes::MEMMAP] = new LogContainer("MM", "Memory Map"); + m_Log[LogTypes::SOUND] = new LogContainer("SND", "Sound"); + m_Log[LogTypes::SAS] = new LogContainer("SAS", "Sound Mixer (Sas)"); + m_Log[LogTypes::HLE] = new LogContainer("HLE", "HLE"); + m_Log[LogTypes::TIMER] = new LogContainer("TMR", "Timer"); + m_Log[LogTypes::VIDEO] = new LogContainer("VID", "Video"); + m_Log[LogTypes::DYNA_REC] = new LogContainer("Jit", "JIT compiler"); + m_Log[LogTypes::NETPLAY] = new LogContainer("NET", "Net play"); + m_Log[LogTypes::ME] = new LogContainer("ME", "Media Engine"); // Remove file logging on small devices -#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) +#ifndef USING_GLES2 m_fileLog = new FileLogListener(File::GetUserPath(F_MAINLOG_IDX).c_str()); m_consoleLog = new ConsoleListener(); m_debuggerLog = new DebuggerLogListener(); @@ -82,7 +82,7 @@ LogManager::LogManager() for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) { m_Log[i]->SetEnable(true); -#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) +#ifndef USING_GLES2 m_Log[i]->AddListener(m_fileLog); m_Log[i]->AddListener(m_consoleLog); #ifdef _MSC_VER @@ -97,11 +97,13 @@ LogManager::~LogManager() { for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) { +#ifndef USING_GLES2 if (m_fileLog != NULL) m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); -#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog); +#ifdef _MSC_VER m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog); +#endif #endif } @@ -109,7 +111,7 @@ LogManager::~LogManager() delete m_Log[i]; if (m_fileLog != NULL) delete m_fileLog; -#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY) +#ifndef USING_GLES2 delete m_consoleLog; delete m_debuggerLog; #endif @@ -214,6 +216,9 @@ void LogContainer::RemoveListener(LogListener *listener) void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) { +#ifdef __SYMBIAN32__ + RDebug::Printf("%s",msg); +#else std::lock_guard lk(m_listeners_lock); std::set::const_iterator i; @@ -221,6 +226,7 @@ void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) { (*i)->Log(level, msg); } +#endif } FileLogListener::FileLogListener(const char *filename) @@ -235,11 +241,7 @@ void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) return; std::lock_guard lk(m_log_lock); -#ifdef __SYMBIAN32__ - RDebug::Printf("%s",msg); -#else m_logfile << msg << std::flush; -#endif } void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index 6b0d089b7ba8..a7119b307c93 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -62,6 +62,7 @@ void* AllocateExecutableMemory(size_t size, bool low) #elif defined(__SYMBIAN32__) //This function may be called more than once, and we want to create only one big //memory chunk for all the executable code for the JIT + void* ptr; if( g_code_chunk == NULL && g_code_heap == NULL) { TInt minsize = SYMBIAN_CODECHUNK_SIZE; @@ -69,8 +70,10 @@ void* AllocateExecutableMemory(size_t size, bool low) g_code_chunk = new RChunk(); g_code_chunk->CreateLocalCode(minsize, maxsize); g_code_heap = UserHeap::ChunkHeap(*g_code_chunk, minsize, 1, maxsize); + ptr = (void*) g_code_heap->Alloc( size ); } - void* ptr = (void*) g_code_heap->Alloc( size ); + else + ptr = g_code_heap->Base(); #else static char *map_hint = 0; #if defined(__x86_64__) && !defined(MAP_32BIT) @@ -116,11 +119,6 @@ void* AllocateExecutableMemory(size_t size, bool low) } #endif -#if defined(_M_X64) - if ((u64)ptr >= 0x80000000 && low == true) - PanicAlert("Executable memory ended up above 2GB!"); -#endif - return ptr; } diff --git a/Common/Thunk.cpp b/Common/Thunk.cpp index 70dc351b9dec..0cc7fb966aaf 100644 --- a/Common/Thunk.cpp +++ b/Common/Thunk.cpp @@ -112,9 +112,9 @@ void *ThunkManager::ProtectFunction(void *function, int num_params) #else SUB(64, R(ESP), Imm8(0x8)); #endif - CALL((void*)save_regs); - CALL((void*)function); - CALL((void*)load_regs); + ABI_CallFunction((void*)save_regs); + ABI_CallFunction((void*)function); + ABI_CallFunction((void*)load_regs); #ifdef _WIN32 ADD(64, R(ESP), Imm8(0x28)); #else diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 3706931498ec..6592c9a63c09 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -21,6 +21,7 @@ set(SRCS ELF/ElfReader.cpp ELF/ParamSFO.cpp ELF/PrxDecrypter.cpp + Font/PGF.cpp HLE/HLE.cpp HLE/HLETables.cpp HLE/sceAtrac.cpp @@ -84,6 +85,7 @@ set(SRCS PSPMixer.cpp System.cpp Core.cpp + ../git-version.cpp ) if(ARM) @@ -114,6 +116,17 @@ add_library(core STATIC ${SRCS}) target_link_libraries(core general common) target_link_libraries(core general base) +# Generate git-version.cpp at build time. +add_custom_target(GitVersion ALL + DEPENDS something_that_never_exists) +add_custom_command(OUTPUT something_that_never_exists + COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/.. + -P ${CMAKE_CURRENT_SOURCE_DIR}/../git-version.cmake) + +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/../git-version.cpp + PROPERTIES GENERATED TRUE) +add_dependencies(core GitVersion) + if(UNIX) add_definitions(-fPIC) add_definitions(-std=gnu++0x) diff --git a/Core/Config.cpp b/Core/Config.cpp index ae887f2cba66..8f027591bdeb 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -49,14 +49,18 @@ void CConfig::Load(const char *iniFileName) general->Get("FirstRun", &bFirstRun, true); general->Get("AutoLoadLast", &bAutoLoadLast, false); general->Get("AutoRun", &bAutoRun, true); + general->Get("Browse", &bBrowse, false); general->Get("ConfirmOnQuit", &bConfirmOnQuit, false); general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true); general->Get("CurrentDirectory", ¤tDirectory, ""); general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false); + // "default" means let emulator decide, "" means disable. + general->Get("ReportHost", &sReportHost, "default"); IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Get("Jit", &bJit, true); - cpu->Get("FastMemory", &bFastMemory, true); + //FastMemory Default set back to True when solve UNIMPL _sceAtracGetContextAddress making game crash + cpu->Get("FastMemory", &bFastMemory, false); IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Get("ShowFPSCounter", &bShowFPSCounter, false); @@ -67,15 +71,17 @@ void CConfig::Load(const char *iniFileName) graphics->Get("LinearFiltering", &bLinearFiltering, false); graphics->Get("SSAA", &SSAntiAliasing, 0); graphics->Get("VBO", &bUseVBO, false); + graphics->Get("FrameSkip", &iFrameSkip, 0); + graphics->Get("UseMediaEngine", &bUseMediaEngine, true); #ifdef USING_GLES2 graphics->Get("AnisotropyLevel", &iAnisotropyLevel, 0); #else graphics->Get("AnisotropyLevel", &iAnisotropyLevel, 8); #endif - graphics->Get("DisableG3DLog", &bDisableG3DLog, false); graphics->Get("VertexCache", &bVertexCache, true); graphics->Get("FullScreen", &bFullScreen, false); graphics->Get("StretchToDisplay", &bStretchToDisplay, false); + graphics->Get("TrueColor", &bTrueColor, true); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); sound->Get("Enable", &bEnableSound, true); @@ -112,10 +118,13 @@ void CConfig::Save() general->Set("FirstRun", bFirstRun); general->Set("AutoLoadLast", bAutoLoadLast); general->Set("AutoRun", bAutoRun); + general->Set("Browse", bBrowse); general->Set("ConfirmOnQuit", bConfirmOnQuit); general->Set("IgnoreBadMemAccess", bIgnoreBadMemAccess); general->Set("CurrentDirectory", currentDirectory); general->Set("ShowDebuggerOnLoad", bShowDebuggerOnLoad); + general->Set("ReportHost", sReportHost); + IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Set("Jit", bJit); cpu->Set("FastMemory", bFastMemory); @@ -129,11 +138,13 @@ void CConfig::Save() graphics->Set("LinearFiltering", bLinearFiltering); graphics->Set("SSAA", SSAntiAliasing); graphics->Set("VBO", bUseVBO); + graphics->Set("FrameSkip", iFrameSkip); + graphics->Set("UseMediaEngine", bUseMediaEngine); graphics->Set("AnisotropyLevel", iAnisotropyLevel); - graphics->Set("DisableG3DLog", bDisableG3DLog); graphics->Set("VertexCache", bVertexCache); graphics->Set("FullScreen", bFullScreen); graphics->Set("StretchToDisplay", bStretchToDisplay); + graphics->Set("TrueColor", bTrueColor); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); sound->Set("Enable", bEnableSound); diff --git a/Core/Config.h b/Core/Config.h index de5437206451..da771cb5d7dc 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -20,8 +20,7 @@ #include #include -#define PPSSPP_VERSION_STR "0.6.1" - +extern const char *PPSSPP_GIT_VERSION; struct SState { @@ -44,11 +43,13 @@ struct CConfig bool bSpeedLimit; bool bConfirmOnQuit; bool bAutoRun; // start immediately + bool bBrowse; // Core bool bIgnoreBadMemAccess; bool bFastMemory; bool bJit; + std::string sReportHost; // GFX bool bDisplayFramebuffer; @@ -58,13 +59,15 @@ struct CConfig bool bLinearFiltering; bool bUseVBO; bool bStretchToDisplay; + int iFrameSkip; // 0 = off; 1 = auto; (future: 2 = skip every 2nd frame; 3 = skip every 3rd frame etc). + bool bUseMediaEngine; int iWindowZoom; // for Windows bool SSAntiAliasing; //for Windows, too - bool bDisableG3DLog; bool bVertexCache; bool bFullScreen; int iAnisotropyLevel; + bool bTrueColor; // Sound bool bEnableSound; diff --git a/Core/Core.cpp b/Core/Core.cpp index fb8f92966c5a..481c1f4b2abc 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -22,6 +22,9 @@ #include "Core.h" #include "MemMap.h" #include "MIPS/MIPS.h" +#ifdef _WIN32 +#include "Windows/OpenGLBase.h" +#endif #include "Host.h" @@ -30,13 +33,24 @@ // HANDLE m_hStepEvent; event m_hStepEvent; recursive_mutex m_hStepMutex; +event m_hInactiveEvent; +recursive_mutex m_hInactiveMutex; // This can be read and written from ANYWHERE. volatile CoreState coreState = CORE_STEPPING; +// Note: intentionally not used for CORE_NEXTFRAME. +volatile bool coreStatePending = false; + +void Core_UpdateState(CoreState newState) +{ + if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING) + coreStatePending = true; + coreState = newState; +} void Core_ErrorPause() { - coreState = CORE_ERROR; + Core_UpdateState(CORE_ERROR); } void Core_Pause() @@ -53,7 +67,7 @@ void Core_Halt(const char *msg) void Core_Stop() { - coreState = CORE_POWERDOWN; + Core_UpdateState(CORE_POWERDOWN); m_hStepEvent.notify_one(); } @@ -62,9 +76,35 @@ bool Core_IsStepping() return coreState == CORE_STEPPING || coreState == CORE_POWERDOWN; } +bool Core_IsInactive() +{ + return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending; +} + +void Core_WaitInactive() +{ + while (!Core_IsInactive()) + m_hInactiveEvent.wait(m_hInactiveMutex); +} + +void Core_WaitInactive(int milliseconds) +{ + while (!Core_IsInactive()) + m_hInactiveEvent.wait_for(m_hInactiveMutex, milliseconds); +} + void Core_RunLoop() { - currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); + while (!coreState) { + currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); + if (coreState == CORE_NEXTFRAME) + { +#ifdef _WIN32 + coreState = CORE_RUNNING; + GL_SwapBuffers(); +#endif + } + } } void Core_DoSingleStep() @@ -98,6 +138,10 @@ void Core_Run() // We should never get here on Android. case CORE_STEPPING: + if (coreStatePending) + m_hInactiveEvent.notify_one(); + coreStatePending = false; + //1: wait for step command.. #if defined(USING_QT_UI) || defined(_DEBUG) host->UpdateDisassembly(); @@ -130,8 +174,13 @@ void Core_Run() case CORE_POWERDOWN: case CORE_ERROR: - case CORE_NEXTFRAME: //1: Exit loop!! + if (coreStatePending) + m_hInactiveEvent.notify_one(); + coreStatePending = false; + return; + + case CORE_NEXTFRAME: return; } } @@ -146,7 +195,7 @@ void Core_EnableStepping(bool step) #if defined(_DEBUG) host->SetDebugMode(true); #endif - coreState = CORE_STEPPING; + Core_UpdateState(CORE_STEPPING); } else { @@ -154,6 +203,7 @@ void Core_EnableStepping(bool step) host->SetDebugMode(false); #endif coreState = CORE_RUNNING; + coreStatePending = false; m_hStepEvent.notify_one(); } } diff --git a/Core/Core.h b/Core/Core.h index d9b07c7f6a10..2b9d359fad27 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -43,5 +43,10 @@ enum CoreState CORE_NEXTFRAME, }; +void Core_UpdateState(CoreState newState); +bool Core_IsInactive(); +void Core_WaitInactive(); +void Core_WaitInactive(int milliseconds); + extern volatile CoreState coreState; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 265a9fd0f461..e12e1fe1069f 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -68,10 +68,16 @@ Disabled ../common;..;../native;../native/ext/glew;../ext/zlib WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + StreamingSIMDExtensions2 + Fast true + + ../Windows/git-version-gen.cmd + Updating git-version.cpp + @@ -79,10 +85,16 @@ Disabled ../common;..;../native;../native/ext/glew;../ext/zlib WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + StreamingSIMDExtensions2 + Fast true + + ../Windows/git-version-gen.cmd + Updating git-version.cpp + @@ -100,6 +112,11 @@ true true + + + ../Windows/git-version-gen.cmd + Updating git-version.cpp + @@ -108,17 +125,24 @@ true true ../common;..;../native;../native/ext/glew;../ext/zlib + StreamingSIMDExtensions2 + Fast true true true + + ../Windows/git-version-gen.cmd + Updating git-version.cpp + + @@ -138,6 +162,7 @@ + @@ -172,6 +197,7 @@ + @@ -270,6 +296,7 @@ + @@ -306,6 +333,7 @@ + @@ -340,6 +368,7 @@ + @@ -403,10 +432,10 @@ + - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index aeafbe3ada86..a063ba33d9bc 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -49,6 +49,9 @@ {0b77054f-7fc7-4c33-ada3-762aecde69e5} + + {1c79e88d-1c48-450b-8af6-f22ce7d40c66} + @@ -375,6 +378,17 @@ MIPS\ARM + + + Font + + + + Core + + + HLE\Libraries + @@ -533,9 +547,6 @@ Util - - Util - Debugger @@ -695,6 +706,16 @@ MIPS\ARM + + + Font + + + Core + + + HLE\Libraries + diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index ce1180bd1be6..77776468b5bf 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -422,12 +422,6 @@ void RemoveAllEvents(int event_type) //This raise only the events required while the fifo is processing data void ProcessFifoWaitEvents() { - if (Common::AtomicLoadAcquire(hasTsEvents)) - MoveEvents(); - - if (!first) - return; - while (first) { if (first->time <= globalTimer) @@ -471,7 +465,7 @@ void MoveEvents() } } -void Advance() +void AdvanceQuick() { int cyclesExecuted = slicelength - currentMIPS->downcount; globalTimer += cyclesExecuted; @@ -495,6 +489,14 @@ void Advance() advanceCallback(cyclesExecuted); } +void Advance() +{ + if (Common::AtomicLoadAcquire(hasTsEvents)) + MoveEvents(); + + AdvanceQuick(); +} + void LogPendingEvents() { Event *ptr = first; diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index 0a05d722c3e3..e61928145015 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -100,6 +100,7 @@ namespace CoreTiming void RemoveAllEvents(int event_type); bool IsScheduled(int event_type); void Advance(); + void AdvanceQuick(); void MoveEvents(); void ProcessFifoWaitEvents(); diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 3aed03de74f1..c3ae09d5d6ee 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -133,20 +133,20 @@ void CBreakPoints::AddBreakPoint(u32 _iAddress, bool temp) void CBreakPoints::InvalidateJit(u32 _iAddress) { // Don't want to clear cache while running, I think? - if (MIPSComp::jit && coreState == CORE_STEPPING) + if (MIPSComp::jit && Core_IsInactive()) MIPSComp::jit->ClearCacheAt(_iAddress); } void CBreakPoints::InvalidateJit() { // Don't want to clear cache while running, I think? - if (MIPSComp::jit && coreState == CORE_STEPPING) + if (MIPSComp::jit && Core_IsInactive()) MIPSComp::jit->ClearCache(); } int CBreakPoints::GetNumBreakpoints() { - return m_iBreakPoints.size(); + return (int)m_iBreakPoints.size(); } int CBreakPoints::GetBreakpointAddress(int i) diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index d6d58c8db843..993fdb4b8a50 100644 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -19,6 +19,7 @@ #include "../Util/PPGeDraw.h" #include "../HLE/sceCtrl.h" #include "../Core/MemMap.h" +#include "Core/Reporting.h" #include "ChunkFile.h" PSPMsgDialog::PSPMsgDialog() @@ -53,6 +54,7 @@ int PSPMsgDialog::Init(unsigned int paramAddr) if(optionsNotCoded) { ERROR_LOG(HLE,"PSPMsgDialog options not coded : 0x%08x",optionsNotCoded); + Reporting::ReportMessage("PSPMsgDialog options not coded: 0x%08x", optionsNotCoded); } flag = 0; @@ -149,14 +151,9 @@ void PSPMsgDialog::DisplayYesNo() } void PSPMsgDialog::DisplayOk() -{ - PPGeDrawText("OK", 250, 150, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFF0000FF)); -} - -void PSPMsgDialog::DisplayEnter() { PPGeDrawImage(okButtonImg, 200, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText("Enter", 230, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Ok", 230, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); } int PSPMsgDialog::Update() @@ -196,11 +193,9 @@ int PSPMsgDialog::Update() if(flag & DS_YESNO) DisplayYesNo(); - if(flag & DS_OK) + if (flag & (DS_OK | DS_VALIDBUTTON)) DisplayOk(); - if(flag & DS_VALIDBUTTON) - DisplayEnter(); if(flag & DS_CANCELBUTTON) DisplayBack(); diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 519291130412..7933c3579ae0 100644 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -20,15 +20,19 @@ #include "../HLE/sceCtrl.h" #include "ChunkFile.h" +#ifndef _WIN32 +#include +#endif + #define NUMKEYROWS 4 #define KEYSPERROW 12 #define NUMBEROFVALIDCHARS (KEYSPERROW * NUMKEYROWS) const char oskKeys[NUMKEYROWS][KEYSPERROW + 1] = { {'1','2','3','4','5','6','7','8','9','0','-','+','\0'}, - {'Q','W','E','R','T','Y','U','I','O','P','[',']','\0'}, - {'A','S','D','F','G','H','J','K','L',';','@','~','\0'}, - {'Z','X','C','V','B','N','M',',','.','/','?','\\','\0'}, + {'q','w','e','r','t','y','u','i','o','p','[',']','\0'}, + {'a','s','d','f','g','h','j','k','l',';','@','~','\0'}, + {'z','x','c','v','b','n','m',',','.','/','?','\\','\0'}, }; @@ -108,9 +112,9 @@ void PSPOskDialog::RenderKeyboard() if (limit <= 0) limit = 16; - const float keyboardLeftSide = (480.0f - (23.0f * KEYSPERROW)) / 2.0f; - float previewLeftSide = (480.0f - (15.0f * limit)) / 2.0f; - float title = (480.0f - (7.0f * limit)) / 2.0f; + const float keyboardLeftSide = (480.0f - (24.0f * KEYSPERROW)) / 2.0f; + float previewLeftSide = (480.0f - (16.0f * limit)) / 2.0f; + float title = (480.0f - limit) / 2.0f; PPGeDrawText(oskDesc.c_str(), title , 20, PPGE_ALIGN_CENTER, 0.5f, CalcFadedColor(0xFFFFFFFF)); for (u32 i = 0; i < limit; ++i) @@ -167,15 +171,19 @@ int PSPOskDialog::Update() StartDraw(); RenderKeyboard(); - PPGeDrawImage(I_CROSS, 100, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText("Select", 130, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawImage(I_CROSS, 30, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Select", 60, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + + PPGeDrawImage(I_CIRCLE, 130, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Delete", 160, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawImage(I_CIRCLE, 200, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText("Delete", 230, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawImage(I_BUTTON, 230, 220, 50, 20, 0, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Start", 245, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Finish", 290, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawImage(I_BUTTON, 290, 220, 50, 20, 0, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText("Start", 305, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText("Finish", 350, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawImage(I_BUTTON, 350, 220, 55, 20, 0, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Select", 365, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText("Caps", 410, 220, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); if (IsButtonPressed(CTRL_UP)) { @@ -203,7 +211,12 @@ int PSPOskDialog::Update() if (IsButtonPressed(CTRL_CROSS)) { if (inputChars.size() < limit) - inputChars += oskKeys[selectedRow][selectedExtra]; + inputChars += (oskKeys[selectedRow][selectedExtra]); + } + else if (IsButtonPressed(CTRL_SELECT)) + { + if (inputChars.size() < limit) + inputChars += toupper(oskKeys[selectedRow][selectedExtra]); } else if (IsButtonPressed(CTRL_CIRCLE)) { @@ -229,7 +242,7 @@ int PSPOskDialog::Update() Memory::Write_U16(value, oskData.outtextPtr + (2 * i)); } - oskData.outtextlength = inputChars.size(); + oskData.outtextlength = (u32)inputChars.size(); oskParams.base.result= 0; oskData.result = PSP_UTILITY_OSK_RESULT_CHANGED; Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData); diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index ecb032d694ec..2849d3a59224 100644 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -20,6 +20,7 @@ #include "../HLE/sceCtrl.h" #include "../Core/MemMap.h" #include "../Config.h" +#include "Core/Reporting.h" PSPSaveDialog::PSPSaveDialog() : PSPDialog() @@ -98,6 +99,7 @@ int PSPSaveDialog::Init(int paramAddr) default: { ERROR_LOG(HLE, "Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str()); + Reporting::ReportMessage("Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str()); param.GetPspParam()->result = 0; status = SCE_UTILITY_STATUS_INITIALIZE; display = DS_NONE; diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 22762fbaa16d..ce3b97089b3b 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -1080,7 +1080,7 @@ bool SavedataParam::CreatePNGIcon(u8* pngData, int pngSize, SaveFileInfo& info) u32 atlasPtr; if (success) atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon"); - if (success && atlasPtr != -1) + if (success && atlasPtr != (u32)-1) { info.textureData = atlasPtr; Memory::Memcpy(atlasPtr, textureData, texSize); diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 77ff90de4dc7..9054e68d6a18 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -217,7 +217,7 @@ bool ElfReader::LoadInto(u32 loadAddress) vaddr = userMemory.Alloc(totalSize, false, "ELF"); } - if (vaddr == -1) { + if (vaddr == (u32)-1) { ERROR_LOG(LOADER, "Failed to allocate memory for ELF!"); return false; } diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 00a163fc5c78..6938ed4fe004 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -18,7 +18,7 @@ #include "ChunkFile.h" #include "FileUtil.h" #include "DirectoryFileSystem.h" - +#include "file/zip_read.h" #ifdef _WIN32 #include @@ -595,3 +595,153 @@ void DirectoryFileSystem::DoState(PointerWrap &p) { ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly."); } } + + +VFSFileSystem::VFSFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) { + INFO_LOG(HLE, "Creating VFS file system"); + hAlloc = _hAlloc; +} + +VFSFileSystem::~VFSFileSystem() { + for (auto iter = entries.begin(); iter != entries.end(); ++iter) { + delete [] iter->second.fileData; + } + entries.clear(); +} + +std::string VFSFileSystem::GetLocalPath(std::string localPath) { + return basePath + localPath; +} + +bool VFSFileSystem::MkDir(const std::string &dirname) { + // NOT SUPPORTED - READ ONLY + return false; +} + +bool VFSFileSystem::RmDir(const std::string &dirname) { + // NOT SUPPORTED - READ ONLY + return false; +} + +bool VFSFileSystem::RenameFile(const std::string &from, const std::string &to) { + // NOT SUPPORTED - READ ONLY + return false; +} + +bool VFSFileSystem::RemoveFile(const std::string &filename) { + // NOT SUPPORTED - READ ONLY + return false; +} + +u32 VFSFileSystem::OpenFile(std::string filename, FileAccess access) { + if (access != FILEACCESS_READ) { + ERROR_LOG(HLE, "VFSFileSystem only supports plain reading"); + return 0; + } + + std::string fullName = GetLocalPath(filename); + const char *fullNameC = fullName.c_str(); + INFO_LOG(HLE,"VFSFileSystem actually opening %s (%s)", fullNameC, filename.c_str()); + + size_t size; + u8 *data = VFSReadFile(fullNameC, &size); + if (!data) { + ERROR_LOG(HLE, "VFSFileSystem failed to open %s", filename.c_str()); + return 0; + } + + OpenFileEntry entry; + entry.fileData = data; + entry.size = size; + entry.seekPos = 0; + u32 newHandle = hAlloc->GetNewHandle(); + entries[newHandle] = entry; + return newHandle; +} + +PSPFileInfo VFSFileSystem::GetFileInfo(std::string filename) { + PSPFileInfo x; + x.name = filename; + + std::string fullName = GetLocalPath(filename); + INFO_LOG(HLE,"Getting VFS file info %s (%s)", fullName.c_str(), filename.c_str()); + FileInfo fo; + VFSGetFileInfo(fullName.c_str(), &fo); + x.exists = fo.exists; + if (x.exists) { + x.size = fo.size; + x.type = fo.isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL; + } + INFO_LOG(HLE,"Got VFS file info: size = %i", (int)x.size); + return x; +} + +void VFSFileSystem::CloseFile(u32 handle) { + EntryMap::iterator iter = entries.find(handle); + if (iter != entries.end()) { + delete [] iter->second.fileData; + entries.erase(iter); + } else { + //This shouldn't happen... + ERROR_LOG(HLE,"Cannot close file that hasn't been opened: %08x", handle); + } +} + +bool VFSFileSystem::OwnsHandle(u32 handle) { + EntryMap::iterator iter = entries.find(handle); + return (iter != entries.end()); +} + +size_t VFSFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) { + INFO_LOG(HLE,"VFSFileSystem::ReadFile %08x %p %i", handle, pointer, (u32)size); + EntryMap::iterator iter = entries.find(handle); + if (iter != entries.end()) + { + size_t bytesRead = size; + memcpy(pointer, iter->second.fileData + iter->second.seekPos, size); + iter->second.seekPos += size; + return bytesRead; + } else { + ERROR_LOG(HLE,"Cannot read file that hasn't been opened: %08x", handle); + return 0; + } +} + +size_t VFSFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) { + // NOT SUPPORTED - READ ONLY + return 0; +} + +size_t VFSFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { + EntryMap::iterator iter = entries.find(handle); + if (iter != entries.end()) { + switch (type) { + case FILEMOVE_BEGIN: iter->second.seekPos = position; break; + case FILEMOVE_CURRENT: iter->second.seekPos += position; break; + case FILEMOVE_END: iter->second.seekPos = iter->second.size + position; break; + } + return iter->second.seekPos; + } else { + //This shouldn't happen... + ERROR_LOG(HLE,"Cannot seek in file that hasn't been opened: %08x", handle); + return 0; + } +} + + +bool VFSFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { + // NOT SUPPORTED + return false; +} + +std::vector VFSFileSystem::GetDirListing(std::string path) { + std::vector myVector; + // TODO + return myVector; +} + +void VFSFileSystem::DoState(PointerWrap &p) { + if (!entries.empty()) { + ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly."); + } +} diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 61ee605406f2..1ee0ac474a6d 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -95,3 +95,41 @@ class DirectoryFileSystem : public IFileSystem { bool FixPathCase(std::string &path, FixPathCaseBehavior behavior); #endif }; + +// VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts. +// Very inefficient - always load the whole file on open. +class VFSFileSystem : public IFileSystem { +public: + VFSFileSystem(IHandleAllocator *_hAlloc, std::string _basePath); + ~VFSFileSystem(); + + void DoState(PointerWrap &p); + std::vector GetDirListing(std::string path); + u32 OpenFile(std::string filename, FileAccess access); + void CloseFile(u32 handle); + size_t ReadFile(u32 handle, u8 *pointer, s64 size); + size_t WriteFile(u32 handle, const u8 *pointer, s64 size); + size_t SeekFile(u32 handle, s32 position, FileMove type); + PSPFileInfo GetFileInfo(std::string filename); + bool OwnsHandle(u32 handle); + + bool MkDir(const std::string &dirname); + bool RmDir(const std::string &dirname); + bool RenameFile(const std::string &from, const std::string &to); + bool RemoveFile(const std::string &filename); + bool GetHostPath(const std::string &inpath, std::string &outpath); + +private: + struct OpenFileEntry { + u8 *fileData; + size_t size; + size_t seekPos; + }; + + typedef std::map EntryMap; + EntryMap entries; + std::string basePath; + IHandleAllocator *hAlloc; + + std::string GetLocalPath(std::string localpath); +}; diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 1c92fa9f039e..09de15ea4dad 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -203,7 +203,6 @@ void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root) bool isFile = (dir.flags & 2) ? false : true; bool relative; - int fnLength = dir.identifierLength; TreeEntry *e = new TreeEntry(); if (dir.identifierLength == 1 && (dir.firstIdChar == '\x00' || dir.firstIdChar == '.')) @@ -560,7 +559,7 @@ std::vector ISOFileSystem::GetDirListing(std::string path) x.size = e->size; x.type = e->isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL; x.isOnSectorSystem = true; - x.startSector = entry->startingPosition/2048; + x.startSector = e->startingPosition/2048; myVector.push_back(x); } return myVector; diff --git a/Core/Font/PGF.cpp b/Core/Font/PGF.cpp new file mode 100644 index 000000000000..635d1021f087 --- /dev/null +++ b/Core/Font/PGF.cpp @@ -0,0 +1,531 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +// ============== NOTE!!!! + +// Thanks to the JPCSP project! This sceFont implementation is basically a C++ take on JPCSP's font code. +// Some parts, especially in this file, were simply copied, so I guess this really makes this file GPL3. + +#include "Common/CommonTypes.h" +#include "Core/MemMap.h" +#include "Core/Font/PGF.h" + +// These fonts, created by ttf2pgf, don't have complete glyph info and need to be identified. +static bool isJPCSPFont(const char *fontName) { + return !strcmp(fontName, "Liberation") || !strcmp(fontName, "Sazanami") || !strcmp(fontName, "UnDotum"); +} + +// Gets a number of bits from an offset. +// TODO: Make more efficient. +static int getBits(int numBits, const u8 *buf, size_t pos) { + int v = 0; + for (int i = 0; i < numBits; i++) { + v = v | (((buf[pos >> 3] >> (pos & 7)) & 1) << i); + pos++; + } + return v; +} + +static std::vector getTable(const u8 *buf, int bpe, int length) { + std::vector vec; + vec.resize(length); + for (int i = 0; i < length; i++) { + vec[i] = getBits(bpe, buf, bpe * i); + } + return vec; +} + +PGF::PGF() + : fontData(0), charMap(0), shadowCharMap(0), charPointerTable(0) { + +} + +PGF::~PGF() { + delete [] fontData; + delete [] charMap; + delete [] shadowCharMap; + delete [] charPointerTable; +} + +void PGF::DoState(PointerWrap &p) { + // TODO! +} + +void PGF::ReadPtr(const u8 *ptr, size_t dataSize) { + const u8 *const startPtr = ptr; + + INFO_LOG(HLE, "Reading %d bytes of PGF header", (int)sizeof(header)); + memcpy(&header, ptr, sizeof(header)); + ptr += sizeof(header); + + if (header.revision == 3) { + memcpy(&rev3extra, ptr, sizeof(rev3extra)); + rev3extra.compCharMapLength1 &= 0xFFFF; + rev3extra.compCharMapLength2 &= 0xFFFF; + ptr += sizeof(rev3extra); + } + + const u32 *wptr = (const u32 *)ptr; + dimensionTable[0].resize(header.dimTableLength); + dimensionTable[1].resize(header.dimTableLength); + for (int i = 0; i < header.dimTableLength; i++) { + dimensionTable[0][i] = *wptr++; + dimensionTable[1][i] = *wptr++; + } + + xAdjustTable[0].resize(header.xAdjustTableLength); + xAdjustTable[1].resize(header.xAdjustTableLength); + for (int i = 0; i < header.xAdjustTableLength; i++) { + xAdjustTable[0][i] = *wptr++; + xAdjustTable[1][i] = *wptr++; + } + + yAdjustTable[0].resize(header.yAdjustTableLength); + yAdjustTable[1].resize(header.yAdjustTableLength); + for (int i = 0; i < header.yAdjustTableLength; i++) { + yAdjustTable[0][i] = *wptr++; + yAdjustTable[1][i] = *wptr++; + } + + advanceTable[0].resize(header.advanceTableLength); + advanceTable[1].resize(header.advanceTableLength); + for (int i = 0; i < header.advanceTableLength; i++) { + advanceTable[0][i] = *wptr++; + advanceTable[1][i] = *wptr++; + } + + const u8 *uptr = (const u8 *)wptr; + + int shadowCharMapSize = ((header.shadowMapLength * header.shadowMapBpe + 31) & ~31) / 8; + shadowCharMap = new u8[shadowCharMapSize]; + for (int i = 0; i < shadowCharMapSize; i++) { + shadowCharMap[i] = *uptr++; + } + + const u16 *sptr = (const u16 *)uptr; + if (header.revision == 3) { + charmapCompressionTable1[0].resize(rev3extra.compCharMapLength1); + charmapCompressionTable1[1].resize(rev3extra.compCharMapLength1); + for (int i = 0; i < rev3extra.compCharMapLength1; i++) { + charmapCompressionTable1[0][i] = *sptr++; + charmapCompressionTable1[1][i] = *sptr++; + } + + charmapCompressionTable2[0].resize(rev3extra.compCharMapLength2); + charmapCompressionTable2[1].resize(rev3extra.compCharMapLength2); + for (int i = 0; i < rev3extra.compCharMapLength2; i++) { + charmapCompressionTable2[0][i] = *sptr++; + charmapCompressionTable2[1][i] = *sptr++; + } + } + + uptr = (const u8 *)sptr; + + int charMapSize = ((header.charMapLength * header.charMapBpe + 31) & ~31) / 8; + + charMap = new u8[charMapSize]; + for (int i = 0; i < charMapSize; i++) { + charMap[i] = *uptr++; + } + + int charPointerSize = (((header.charPointerLength * header.charPointerBpe + 31) & ~31) / 8); + charPointerTable = new u8[charPointerSize]; + for (int i = 0; i < charPointerSize; i++) { + charPointerTable[i] = *uptr++; + } + + // PGF Fontdata. + u32 fontDataOffset = uptr - startPtr; + + fontDataSize = dataSize - fontDataOffset; + fontData = new u8[fontDataSize]; + memcpy(fontData, uptr, fontDataSize); + + // charmap.resize(); + charmap.resize(header.charMapLength); + int charmap_compr_len = header.revision == 3 ? 7 : 1; + charmap_compr.resize(charmap_compr_len * 4); + glyphs.resize(header.charPointerLength); + shadowGlyphs.resize(header.shadowMapLength); + firstGlyph = header.firstGlyph; + + // Parse out the char map (array where each entry is an irregular number of bits) + // BPE = bits per entry, I think. + for (int i = 0; i < header.charMapLength; i++) { + charmap[i] = getBits(header.charMapBpe, charMap, i * header.charMapBpe); + // This check seems a little odd. + if ((size_t)charmap[i] >= glyphs.size()) + charmap[i] = 65535; + } + + std::vector charPointers = getTable(charPointerTable, header.charPointerBpe, glyphs.size()); + std::vector shadowMap = getTable(shadowCharMap, header.shadowMapBpe, shadowGlyphs.size()); + + // Pregenerate glyphs. + for (size_t i = 0; i < glyphs.size(); i++) { + GetGlyph(fontData, charPointers[i] * 4 * 8 /* ??? */, FONT_PGF_CHARGLYPH, glyphs[i]); + } + + // And shadow glyphs. + for (size_t i = 0; i < shadowGlyphs.size(); i++) { + size_t shadowId = glyphs[i].shadowID; + if (shadowId < shadowMap.size()) { + size_t charId = shadowMap[shadowId]; + if (charId < glyphs.size()) { + // TODO: check for pre existing shadow glyph + GetGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, FONT_PGF_SHADOWGLYPH, shadowGlyphs[i]); + } + } + } +} + +int PGF::GetCharIndex(int charCode, const std::vector &charmapCompressed) { + int charIndex = 0; + for (size_t i = 0; i < charmapCompressed.size(); i += 2) { + if (charCode >= charmapCompressed[i] && charCode < charmapCompressed[i] + charmapCompressed[i + 1]) { + charIndex += charCode - charmapCompressed[i]; + return charIndex; + } + charIndex += charmapCompressed[i + 1]; + } + return -1; +} + +bool PGF::GetCharInfo(int charCode, PGFCharInfo *charInfo) { + Glyph glyph; + if (!GetCharGlyph(charCode, FONT_PGF_CHARGLYPH, glyph)) { + // Character not in font, return zeroed charInfo as on real PSP. + return false; + } + memset(charInfo, 0, sizeof(*charInfo)); + + charInfo->bitmapWidth = glyph.w; + charInfo->bitmapHeight = glyph.h; + charInfo->bitmapLeft = glyph.left; + charInfo->bitmapTop = glyph.top; + charInfo->sfp26Width = glyph.dimensionWidth; + charInfo->sfp26Height = glyph.dimensionHeight; + charInfo->sfp26Ascender = glyph.top << 6; + charInfo->sfp26Descender = (glyph.h - glyph.top) << 6; + charInfo->sfp26BearingHX = glyph.xAdjustH; + charInfo->sfp26BearingHY = glyph.yAdjustH; + charInfo->sfp26BearingVX = glyph.xAdjustV; + charInfo->sfp26BearingVY = glyph.yAdjustV; + charInfo->sfp26AdvanceH = glyph.advanceH; + charInfo->sfp26AdvanceV = glyph.advanceV; + return true; +} + +void PGF::GetFontInfo(PGFFontInfo *fi) { + fi->maxGlyphWidthI = header.maxSize[0]; + fi->maxGlyphHeightI = header.maxSize[1]; + fi->maxGlyphAscenderI = header.maxAscender; + fi->maxGlyphDescenderI = header.maxDescender; + fi->maxGlyphLeftXI = header.maxLeftXAdjust; + fi->maxGlyphBaseYI = header.maxBaseYAdjust; + fi->minGlyphCenterXI = header.minCenterXAdjust; + fi->maxGlyphTopYI = header.maxTopYAdjust; + fi->maxGlyphAdvanceXI = header.maxAdvance[0]; + fi->maxGlyphAdvanceYI = header.maxAdvance[1]; + fi->maxGlyphWidthF = header.maxSize[0] / 64.0f; + fi->maxGlyphHeightF = header.maxSize[1] / 64.0f; + fi->maxGlyphAscenderF = header.maxAscender / 64.0f; + fi->maxGlyphDescenderF = header.maxDescender / 64.0f; + fi->maxGlyphLeftXF = header.maxLeftXAdjust / 64.0f; + fi->maxGlyphBaseYF = header.maxBaseYAdjust / 64.0f; + fi->minGlyphCenterXF = header.minCenterXAdjust / 64.0f; + fi->maxGlyphTopYF = header.maxTopYAdjust / 64.0f; + fi->maxGlyphAdvanceXF = header.maxAdvance[0] / 64.0f; + fi->maxGlyphAdvanceYF = header.maxAdvance[1] / 64.0f; + + fi->maxGlyphWidth = header.maxGlyphWidth; + fi->maxGlyphHeight = header.maxGlyphHeight; + fi->charMapLength = header.charMapLength; + fi->shadowMapLength = 0; // header.shadowMapLength; TODO + + fi->BPP = header.bpp; +} + +bool PGF::GetGlyph(const u8 *fontdata, size_t charPtr, int glyphType, Glyph &glyph) { + if (glyphType == FONT_PGF_SHADOWGLYPH) { + if (charPtr + 96 > fontDataSize * 8) + return false; + charPtr += getBits(14, fontdata, charPtr) * 8; + if (charPtr + 96 > fontDataSize * 8) + return false; + } + charPtr += 14; + + glyph.w = getBits(7, fontdata, charPtr); + charPtr += 7; + + glyph.h = getBits(7, fontdata, charPtr); + charPtr += 7; + + glyph.left = getBits(7, fontdata, charPtr); + charPtr += 7; + if (glyph.left >= 64) { + glyph.left -= 128; + } + + glyph.top = getBits(7, fontdata, charPtr); + charPtr += 7; + if (glyph.top >= 64) { + glyph.top -= 128; + } + + glyph.flags = getBits(6, fontdata, charPtr); + charPtr += 6; + + if (glyph.flags & FONT_PGF_CHARGLYPH) { + // Skip magic number + charPtr += 7; + + glyph.shadowID = getBits(9, fontdata, charPtr); + charPtr += 9; + + int dimensionIndex = getBits(8, fontdata, charPtr); + charPtr += 8; + + int xAdjustIndex = getBits(8, fontdata, charPtr); + charPtr += 8; + + int yAdjustIndex = getBits(8, fontdata, charPtr); + charPtr += 8; + + charPtr += + ((glyph.flags & FONT_PGF_METRIC_FLAG1) ? 0 : 56) + + ((glyph.flags & FONT_PGF_METRIC_FLAG2) ? 0 : 56) + + ((glyph.flags & FONT_PGF_METRIC_FLAG3) ? 0 : 56); + + int advanceIndex = getBits(8, fontdata, charPtr); + charPtr += 8; + + if (dimensionIndex < header.dimTableLength) { + glyph.dimensionWidth = dimensionTable[0][dimensionIndex]; + glyph.dimensionHeight = dimensionTable[1][dimensionIndex]; + } + + if (xAdjustIndex < header.xAdjustTableLength) { + glyph.xAdjustH = xAdjustTable[0][xAdjustIndex]; + glyph.xAdjustV = xAdjustTable[1][xAdjustIndex]; + } + + if (yAdjustIndex < header.xAdjustTableLength) { + glyph.yAdjustH = yAdjustTable[0][yAdjustIndex]; + glyph.yAdjustV = yAdjustTable[1][yAdjustIndex]; + } + + if (dimensionIndex == 0 && xAdjustIndex == 0 && yAdjustIndex == 0 && isJPCSPFont(fileName.c_str())) { + // Fonts created by ttf2pgf do not contain complete Glyph information. + // Provide default values. + glyph.dimensionWidth = glyph.w << 6; + glyph.dimensionHeight = glyph.h << 6; + // This stuff doesn't exactly look right. + glyph.xAdjustH = glyph.left << 6; + glyph.xAdjustV = glyph.left << 6; + glyph.yAdjustH = glyph.top << 6; + glyph.yAdjustV = glyph.top << 6; + } + + if (advanceIndex < header.advanceTableLength) { + glyph.advanceH = advanceTable[0][advanceIndex]; + glyph.advanceV = advanceTable[1][advanceIndex]; + } + } else { + glyph.shadowID = 65535; + glyph.advanceH = 0; + } + + glyph.ptr = (u32)(charPtr / 8); + return true; +} + +bool PGF::GetCharGlyph(int charCode, int glyphType, Glyph &glyph) { + if (charCode < firstGlyph) + return false; + charCode -= firstGlyph; + if (charCode < (int)charmap.size()) { + charCode = charmap[charCode]; + } + if (glyphType == FONT_PGF_CHARGLYPH) { + if (charCode >= (int)glyphs.size()) + return false; + glyph = glyphs[charCode]; + } else { + if (charCode >= (int)shadowGlyphs.size()) + return false; + glyph = shadowGlyphs[charCode]; + } + return true; +} + +void PGF::DrawCharacter(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, int clipX, int clipY, int clipWidth, int clipHeight, int pixelformat, int charCode, int altCharCode, int glyphType) { + Glyph glyph; + if (!GetCharGlyph(charCode, glyphType, glyph)) { + // No Glyph available for this charCode, try to use the alternate char. + charCode = altCharCode; + if (!GetCharGlyph(charCode, glyphType, glyph)) { + return; + } + } + + if (glyph.w <= 0 || glyph.h <= 0) { + return; + } + + if (((glyph.flags & FONT_PGF_BMP_OVERLAY) != FONT_PGF_BMP_H_ROWS) && + ((glyph.flags & FONT_PGF_BMP_OVERLAY) != FONT_PGF_BMP_V_ROWS)) { + return; + } + + u32 bitPtr = glyph.ptr * 8; + int numberPixels = glyph.w * glyph.h; + int pixelIndex = 0; + + while (pixelIndex < numberPixels && bitPtr + 8 < fontDataSize * 8) { + // This is some kind of nibble based RLE compression. + int nibble = getBits(4, fontData, bitPtr); + bitPtr += 4; + + int count; + int value; + if (nibble < 8) { + value = getBits(4, fontData, bitPtr); + bitPtr += 4; + count = nibble + 1; + } else { + count = 16 - nibble; + } + + for (int i = 0; i < count && pixelIndex < numberPixels; i++) { + if (nibble >= 8) { + value = getBits(4, fontData, bitPtr); + bitPtr += 4; + } + + int xx, yy; + if ((glyph.flags & FONT_PGF_BMP_OVERLAY) == FONT_PGF_BMP_H_ROWS) { + xx = pixelIndex % glyph.w; + yy = pixelIndex / glyph.w; + } else { + xx = pixelIndex / glyph.h; + yy = pixelIndex % glyph.h; + } + + int pixelX = x + xx; + int pixelY = y + yy; + if (pixelX >= clipX && pixelX < clipX + clipWidth && pixelY >= clipY && pixelY < clipY + clipHeight) { + // 4-bit color value + int pixelColor = value; + switch (pixelformat) { + case PSP_FONT_PIXELFORMAT_8: + // 8-bit color value + pixelColor |= pixelColor << 4; + break; + case PSP_FONT_PIXELFORMAT_24: + // 24-bit color value + pixelColor |= pixelColor << 4; + pixelColor |= pixelColor << 8; + pixelColor |= pixelColor << 8; + break; + case PSP_FONT_PIXELFORMAT_32: + // 32-bit color value + pixelColor |= pixelColor << 4; + pixelColor |= pixelColor << 8; + pixelColor |= pixelColor << 16; + break; + } + + SetFontPixel(base, bpl, bufWidth, bufHeight, pixelX, pixelY, pixelColor, pixelformat); + } + + pixelIndex++; + } + } +} + +void PGF::SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, int pixelColor, int pixelformat) { + if (x < 0 || x >= bufWidth || y < 0 || y >= bufHeight) { + return; + } + + static const u8 fontPixelSizeInBytes[] = { 0, 0, 1, 3, 4 }; // 0 means 2 pixels per byte + int pixelBytes = fontPixelSizeInBytes[pixelformat]; + int bufMaxWidth = (pixelBytes == 0 ? bpl * 2 : bpl / pixelBytes); + if (x >= bufMaxWidth) { + return; + } + + int framebufferAddr = base + (y * bpl) + (pixelBytes == 0 ? x / 2 : x * pixelBytes); + + switch (pixelformat) { + case PSP_FONT_PIXELFORMAT_4: + case PSP_FONT_PIXELFORMAT_4_REV: + { + int oldColor = Memory::Read_U8(framebufferAddr); + int newColor; + if ((x & 1) != pixelformat) { + newColor = (pixelColor << 4) | (oldColor & 0xF); + } else { + newColor = (oldColor & 0xF0) | pixelColor; + } + Memory::Write_U8(newColor, framebufferAddr); + break; + } + case PSP_FONT_PIXELFORMAT_8: + { + Memory::Write_U8((u8)pixelColor, framebufferAddr); + break; + } + case PSP_FONT_PIXELFORMAT_24: + { + Memory::Write_U8(pixelColor & 0xFF, framebufferAddr + 0); + Memory::Write_U8(pixelColor >> 8, framebufferAddr + 1); + Memory::Write_U8(pixelColor >> 16, framebufferAddr + 2); + break; + } + case PSP_FONT_PIXELFORMAT_32: + { + Memory::Write_U32(pixelColor, framebufferAddr); + break; + } + } +} + +u32 GetFontPixelColor(int color, int pixelformat) { + switch (pixelformat) { + case PSP_FONT_PIXELFORMAT_4: + case PSP_FONT_PIXELFORMAT_4_REV: + // Use only 4-bit alpha + color = (color >> 28) & 0xF; + break; + case PSP_FONT_PIXELFORMAT_8: + // Use only 8-bit alpha + color = (color >> 24) & 0xFF; + break; + case PSP_FONT_PIXELFORMAT_24: + // Use RGB with 8-bit values + color = color & 0x00FFFFFF; + break; + case PSP_FONT_PIXELFORMAT_32: + // Use RGBA with 8-bit values + break; + } + + return color; +} \ No newline at end of file diff --git a/Core/Font/PGF.h b/Core/Font/PGF.h new file mode 100644 index 000000000000..bba9f29d3d43 --- /dev/null +++ b/Core/Font/PGF.h @@ -0,0 +1,289 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +// Thanks to the JPCSP project! This sceFont implementation is basically a C++ take on JPCSP's font code. + +#pragma once + +#include +#include + +#include "Common/Log.h" +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" + +enum { + FONT_FILETYPE_PGF = 0x00, + FONT_FILETYPE_BWFON = 0x01, +}; + +enum { + FONT_PGF_BMP_H_ROWS = 0x01, + FONT_PGF_BMP_V_ROWS = 0x02, + FONT_PGF_BMP_OVERLAY = 0x03, + FONT_PGF_METRIC_FLAG1 = 0x04, + FONT_PGF_METRIC_FLAG2 = 0x08, + FONT_PGF_METRIC_FLAG3 = 0x10, + FONT_PGF_CHARGLYPH = 0x20, + FONT_PGF_SHADOWGLYPH = 0x40, +}; + +enum Family { + FONT_FAMILY_SANS_SERIF = 1, + FONT_FAMILY_SERIF = 2, +}; + +enum Style { + FONT_STYLE_REGULAR = 1, + FONT_STYLE_ITALIC = 2, + FONT_STYLE_BOLD = 5, + FONT_STYLE_BOLD_ITALIC = 6, + FONT_STYLE_DB = 103, // Demi-Bold / semi-bold +}; + +enum Language { + FONT_LANGUAGE_JAPANESE = 1, + FONT_LANGUAGE_LATIN = 2, + FONT_LANGUAGE_KOREAN = 3, + FONT_LANGUAGE_CHINESE = 4, +}; + +enum FontPixelFormat { + PSP_FONT_PIXELFORMAT_4 = 0, // 2 pixels packed in 1 byte (natural order) + PSP_FONT_PIXELFORMAT_4_REV = 1, // 2 pixels packed in 1 byte (reversed order) + PSP_FONT_PIXELFORMAT_8 = 2, // 1 pixel in 1 byte + PSP_FONT_PIXELFORMAT_24 = 3, // 1 pixel in 3 bytes (RGB) + PSP_FONT_PIXELFORMAT_32 = 4, // 1 pixel in 4 bytes (RGBA) +}; + + +struct PGFFontStyle { + float fontH; + float fontV; + float fontHRes; + float fontVRes; + float fontWeight; + u16 fontFamily; + u16 fontStyle; + // Check. + u16 fontStyleSub; + u16 fontLanguage; + u16 fontRegion; + u16 fontCountry; + char fontName[64]; + char fontFileName[64]; + u32 fontAttributes; + u32 fontExpire; +}; + + +class Glyph { +public: + int x; + int y; + int w; + int h; + int left; + int top; + int flags; + int shadowID; + int advanceH; + int advanceV; + int dimensionWidth, dimensionHeight; + int xAdjustH, xAdjustV; + int yAdjustH, yAdjustV; + u32 ptr; +}; + +#pragma pack(push,1) +struct PGFHeader +{ + u16 headerOffset; + u16 headerSize; + + char PGFMagic[4]; + int revision; + int version; + + int charMapLength; + int charPointerLength; + int charMapBpe; + int charPointerBpe; + + u8 pad1[2]; + u8 bpp; + u8 pad2[1]; + + int hSize; + int vSize; + int hResolution; + int vResolution; + + u8 pad3[1]; + char fontName[64]; + char fontType[64]; + u8 pad4[1]; + + u16 firstGlyph; + u16 lastGlyph; + + u8 pad5[26]; + + int maxAscender; + int maxDescender; + int maxLeftXAdjust; + int maxBaseYAdjust; + int minCenterXAdjust; + int maxTopYAdjust; + + int maxAdvance[2]; + int maxSize[2]; + u16 maxGlyphWidth; + u16 maxGlyphHeight; + u8 pad6[2]; + + u8 dimTableLength; + u8 xAdjustTableLength; + u8 yAdjustTableLength; + u8 advanceTableLength; + u8 pad7[102]; + + int shadowMapLength; + int shadowMapBpe; + float unknown1; + int shadowScale[2]; + u8 pad8[8]; +}; + +struct PGFHeaderRev3Extra { + int compCharMapBpe1; + int compCharMapLength1; + int compCharMapBpe2; + int compCharMapLength2; + u32 unknown; +}; + +struct PGFCharInfo { + u32 bitmapWidth; + u32 bitmapHeight; + u32 bitmapLeft; + u32 bitmapTop; + // Glyph metrics (in 26.6 signed fixed-point). + u32 sfp26Width; + u32 sfp26Height; + s32 sfp26Ascender; + s32 sfp26Descender; + s32 sfp26BearingHX; + s32 sfp26BearingHY; + s32 sfp26BearingVX; + s32 sfp26BearingVY; + s32 sfp26AdvanceH; + s32 sfp26AdvanceV; + u8 pad[4]; +}; + +struct PGFFontInfo { + // Glyph metrics (in 26.6 signed fixed-point). + int maxGlyphWidthI; + int maxGlyphHeightI; + int maxGlyphAscenderI; + int maxGlyphDescenderI; + int maxGlyphLeftXI; + int maxGlyphBaseYI; + int minGlyphCenterXI; + int maxGlyphTopYI; + int maxGlyphAdvanceXI; + int maxGlyphAdvanceYI; + + // Glyph metrics (replicated as float). + float maxGlyphWidthF; + float maxGlyphHeightF; + float maxGlyphAscenderF; + float maxGlyphDescenderF; + float maxGlyphLeftXF; + float maxGlyphBaseYF; + float minGlyphCenterXF; + float maxGlyphTopYF; + float maxGlyphAdvanceXF; + float maxGlyphAdvanceYF; + + // Bitmap dimensions. + short maxGlyphWidth; + short maxGlyphHeight; + int charMapLength; // Number of elements in the font's charmap. + int shadowMapLength; // Number of elements in the font's shadow charmap. + + // Font style (used by font comparison functions). + PGFFontStyle fontStyle; + + int BPP; // Font's BPP. +}; + +#pragma pack(pop) + +class PGF { +public: + PGF(); + ~PGF(); + + void ReadPtr(const u8 *ptr, size_t dataSize); + + bool GetCharInfo(int charCode, PGFCharInfo *ci); + void GetFontInfo(PGFFontInfo *fi); + void DrawCharacter(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, int clipX, int clipY, int clipWidth, int clipHeight, int pixelformat, int charCode, int altCharCode, int glyphType); + + void DoState(PointerWrap &p); + + PGFHeader header; + +private: + bool GetGlyph(const u8 *fontdata, size_t charPtr, int glyphType, Glyph &glyph); + bool GetCharGlyph(int charCode, int glyphType, Glyph &glyph); + + // Unused + int GetCharIndex(int charCode, const std::vector &charmapCompressed); + + void SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, int pixelColor, int pixelformat); + + PGFHeaderRev3Extra rev3extra; + + // Font character image data + u8 *fontData; + size_t fontDataSize; + + std::string fileName; + + std::vector dimensionTable[2]; + std::vector xAdjustTable[2]; + std::vector yAdjustTable[2]; + std::vector advanceTable[2]; + + // Unused + std::vector charmapCompressionTable1[2]; + std::vector charmapCompressionTable2[2]; + + u8 *charMap; + u8 *shadowCharMap; + u8 *charPointerTable; + + std::vector charmap_compr; + std::vector charmap; + + std::vector glyphs; + std::vector shadowGlyphs; + int firstGlyph; +}; diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index 85468754b8cf..95a13616f578 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -96,6 +96,11 @@ template void WrapF_V() { RETURNF(func()); } +// TODO: Not sure about the floating point parameter passing +template void WrapF_IFU() { + RETURNF(func(PARAM(0), PARAMF(0), PARAM(1))); +} + template void WrapU_U() { u32 retval = func(PARAM(0)); RETURN(retval); @@ -133,7 +138,7 @@ template void WrapI_UU() { template void WrapI_UFF() { // Not sure about the float arguments. - int retval = func(PARAM(0), currentMIPS->f[0], currentMIPS->f[1]); + int retval = func(PARAM(0), currentMIPS->f[12], currentMIPS->f[13]); RETURN(retval); } @@ -313,6 +318,11 @@ template void WrapI_IIII() { RETURN(retval); } +template void WrapI_ICIUU() { + int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); + RETURN(retval); +} + template void WrapI_IIU() { int retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); @@ -481,6 +491,11 @@ template void WrapU_UUUU() { RETURN(retval); } +template void WrapU_UCUU() { + u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3)); + RETURN(retval); +} + template void WrapU_UUUI() { u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)); RETURN(retval); diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 6e7087c04d17..faeea8ead483 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -21,6 +21,7 @@ #include #include "../MemMap.h" #include "../Config.h" +#include "Core/Reporting.h" #include "HLETables.h" #include "../System.h" @@ -64,7 +65,7 @@ void HLEInit() void HLEDoState(PointerWrap &p) { - Syscall sc = {0}; + Syscall sc = {""}; p.Do(unresolvedSyscalls, sc); p.Do(exportedCalls, sc); p.DoMarker("HLE"); @@ -164,6 +165,7 @@ u32 GetSyscallOp(const char *moduleName, u32 nib) else { INFO_LOG(HLE, "Syscall (%s, %08x) unknown", moduleName, nib); + Reporting::ReportMessage("Unknown syscall in known module: %s 0x%08x", moduleName, nib); return (0x0003FFCC | (modindex<<18)); // invalid syscall } } @@ -297,7 +299,7 @@ bool hleExecuteDebugBreak(const HLEFunction &func) // Never break on these, they're noise. u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE}; - for (int i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i) + for (size_t i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i) { if (func.ID == blacklistedNIDs[i]) return false; diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 8ff39c328c63..40b4a17dbc60 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -53,11 +53,12 @@ struct Syscall }; #define PARAM(n) currentMIPS->r[4+n] +#define PARAMF(n) currentMIPS->f[12+n] #define RETURN(n) currentMIPS->r[2]=n #define RETURNF(fl) currentMIPS->f[0]=fl #ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0]) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #endif #include "FunctionWrappers.h" diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 3f9597b443e7..1086f2a5d577 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -54,6 +54,7 @@ #include "sceVaudio.h" #include "sceUsb.h" #include "sceChnnlsv.h" +#include "scePspNpDrm_user.h" #define N(s) s @@ -246,6 +247,7 @@ void RegisterAllModules() { Register_sceVaudio(); Register_sceUsb(); Register_sceChnnlsv(); + Register_sceNpDrm(); for (int i = 0; i < numModules; i++) { diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 4e6604add6ef..6e05803c5bd6 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -187,7 +187,7 @@ u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF ret = ATRAC_ERROR_ALL_DATA_DECODED; } else { // TODO: This isn't at all right, but at least it makes the music "last" some time. - int numSamples = (atrac->decodeEnd - atrac->decodePos) / (sizeof(s16) * 2); + u32 numSamples = (atrac->decodeEnd - atrac->decodePos) / (sizeof(s16) * 2); if (atrac->decodePos >= atrac->decodeEnd) { numSamples = 0; } else if (numSamples > ATRAC_MAX_SAMPLES) { @@ -395,10 +395,11 @@ u32 sceAtracGetStreamDataInfo(int atracID, u32 writeAddr, u32 writableBytesAddr, Atrac *atrac = getAtrac(atracID); if (!atrac) { //return -1; + } else { + Memory::Write_U32(atrac->first.addr, writeAddr); + Memory::Write_U32(atrac->first.writableBytes, writableBytesAddr); + Memory::Write_U32(atrac->first.fileoffset, readOffsetAddr); } - Memory::Write_U32(atrac->first.addr, writeAddr); - Memory::Write_U32(atrac->first.writableBytes, writableBytesAddr); - Memory::Write_U32(atrac->first.fileoffset, readOffsetAddr); return 0; } @@ -554,7 +555,7 @@ int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) { - ERROR_LOG(HLE, "UNIMPL sceAtracLowLevelDecode(%i, %i, %08x, %08x, %08x, %08x)", atracID, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr); + ERROR_LOG(HLE, "UNIMPL sceAtracLowLevelDecode(%i, %08x, %08x, %08x, %08x)", atracID, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr); return 0; } diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 0d7e1eedc86f..888db3ef0582 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -184,6 +184,7 @@ u32 sceAudioChRelease(u32 chan) { return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; } DEBUG_LOG(HLE, "sceAudioChRelease(%i)", chan); + chans[chan].clear(); chans[chan].reserved = false; return 1; } @@ -271,6 +272,7 @@ u32 sceAudioOutput2GetRestSample(){ u32 sceAudioOutput2Release(){ DEBUG_LOG(HLE,"sceAudioOutput2Release()"); + chans[0].clear(); chans[0].reserved = false; return 0; } @@ -302,6 +304,7 @@ u32 sceAudioSRCChReserve(u32 sampleCount, u32 freq, u32 format) { u32 sceAudioSRCChRelease() { DEBUG_LOG(HLE, "sceAudioSRCChRelease()"); + chans[src].clear(); chans[src].reserved = false; return 0; } diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 819b884819f8..b79c9733dbcb 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -35,7 +35,7 @@ const int PSP_CTRL_ERROR_INVALID_MODE = 0x80000107; const int PSP_CTRL_ERROR_INVALID_IDLE_PTR = 0x80000023; -const int NUM_CTRL_BUFFERS = 64; +const u32 NUM_CTRL_BUFFERS = 64; enum { @@ -68,8 +68,8 @@ static u32 ctrlOldButtons = 0; static _ctrl_data ctrlBufs[NUM_CTRL_BUFFERS]; static _ctrl_data ctrlCurrent; -static int ctrlBuf = 0; -static int ctrlBufRead = 0; +static u32 ctrlBuf = 0; +static u32 ctrlBufRead = 0; static CtrlLatch latch; static int ctrlIdleReset = -1; @@ -189,7 +189,7 @@ int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek) if (nBufs > NUM_CTRL_BUFFERS) return SCE_KERNEL_ERROR_INVALID_SIZE; - int resetRead = ctrlBufRead; + u32 resetRead = ctrlBufRead; u32 availBufs; // Peeks always work, they just go go from now X buffers. @@ -280,7 +280,7 @@ void __CtrlInit() ctrlCurrent.analog[0] = 128; ctrlCurrent.analog[1] = 128; - for (int i = 0; i < NUM_CTRL_BUFFERS; i++) + for (u32 i = 0; i < NUM_CTRL_BUFFERS; i++) memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(_ctrl_data)); } diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 86bf36750a94..8fc50a142c69 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -80,14 +80,20 @@ static bool framebufIsLatched; static int enterVblankEvent = -1; static int leaveVblankEvent = -1; +static int afterFlipEvent = -1; static int hCount; static int hCountTotal; //unused static int vCount; static int isVblank; +static int numSkippedFrames; static bool hasSetMode; +static int mode; +static int width; +static int height; // Don't include this in the state, time increases regardless of state. -static double lastFrameTime; +static double curFrameTime; +static double nextFrameTime; std::vector vblankWaitingThreads; @@ -107,10 +113,15 @@ enum { void hleEnterVblank(u64 userdata, int cyclesLate); void hleLeaveVblank(u64 userdata, int cyclesLate); +void hleAfterFlip(u64 userdata, int cyclesLate); void __DisplayInit() { gpuStats.reset(); hasSetMode = false; + mode = 0; + width = 480; + height = 272; + numSkippedFrames = 0; framebufIsLatched = false; framebuf.topaddr = 0x04000000; framebuf.pspFramebufFormat = PSP_DISPLAY_PIXEL_FORMAT_8888; @@ -118,13 +129,15 @@ void __DisplayInit() { enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank); leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank); + afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip); CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0); isVblank = 0; vCount = 0; hCount = 0; hCountTotal = 0; - lastFrameTime = 0; + curFrameTime = 0.0; + nextFrameTime = 0.0; InitGfxState(); } @@ -138,6 +151,9 @@ void __DisplayDoState(PointerWrap &p) { p.Do(vCount); p.Do(isVblank); p.Do(hasSetMode); + p.Do(mode); + p.Do(width); + p.Do(height); WaitVBlankInfo wvi(0); p.Do(vblankWaitingThreads, wvi); @@ -145,6 +161,8 @@ void __DisplayDoState(PointerWrap &p) { CoreTiming::RestoreRegisterEvent(enterVblankEvent, "EnterVBlank", &hleEnterVblank); p.Do(leaveVblankEvent); CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank); + p.Do(afterFlipEvent); + CoreTiming::RestoreRegisterEvent(afterFlipEvent, "AfterFlipEVent", &hleAfterFlip); p.Do(gstate); p.Do(gstate_c); @@ -180,13 +198,13 @@ void __DisplayFireVblank() { } } -float calculateFPS() +void CalculateFPS() { static double highestFps = 0.0; static int lastFpsFrame = 0; static double lastFpsTime = 0.0; static double fps = 0.0; - + time_update(); double now = time_now_d(); @@ -199,9 +217,148 @@ float calculateFPS() lastFpsFrame = gpuStats.numFrames; lastFpsTime = now; } - return fps; + + char stats[50]; + sprintf(stats, "VPS: %0.1f", fps); + + #ifdef USING_GLES2 + float zoom = 0.7f; /// g_Config.iWindowZoom; + float soff = 0.7f; + #else + float zoom = 0.5f; /// g_Config.iWindowZoom; + float soff = 0.5f; + #endif + PPGeBegin(); + PPGeDrawText(stats, 476 + soff, 4 + soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000); + PPGeDrawText(stats, 476 + -soff, 4 -soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000); + PPGeDrawText(stats, 476, 4, PPGE_ALIGN_RIGHT, zoom, 0xFF30FF30); + PPGeEnd(); +} + +void DebugStats() +{ + gpu->UpdateStats(); + char stats[2048]; + + sprintf(stats, + "Frames: %i\n" + "DL processing time: %0.2f ms\n" + "Kernel processing time: %0.2f ms\n" + "Slowest syscall: %s : %0.2f ms\n" + "Most active syscall: %s : %0.2f ms\n" + "Draw calls: %i, flushes %i\n" + "Cached Draw calls: %i\n" + "Num Tracked Vertex Arrays: %i\n" + "Vertices Submitted: %i\n" + "Cached Vertices Drawn: %i\n" + "Uncached Vertices Drawn: %i\n" + "FBOs active: %i\n" + "Textures active: %i, decoded: %i\n" + "Texture invalidations: %i\n" + "Vertex shaders loaded: %i\n" + "Fragment shaders loaded: %i\n" + "Combined shaders loaded: %i\n", + gpuStats.numFrames, + gpuStats.msProcessingDisplayLists * 1000.0f, + kernelStats.msInSyscalls * 1000.0f, + kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", + kernelStats.slowestSyscallTime * 1000.0f, + kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", + kernelStats.summedSlowestSyscallTime * 1000.0f, + gpuStats.numDrawCalls, + gpuStats.numFlushes, + gpuStats.numCachedDrawCalls, + gpuStats.numTrackedVertexArrays, + gpuStats.numVertsSubmitted, + gpuStats.numCachedVertsDrawn, + gpuStats.numUncachedVertsDrawn, + gpuStats.numFBOs, + gpuStats.numTextures, + gpuStats.numTexturesDecoded, + gpuStats.numTextureInvalidations, + gpuStats.numVertexShaders, + gpuStats.numFragmentShaders, + gpuStats.numShaders + ); + + #ifdef USING_GLES2 + float zoom = 0.5f; /// g_Config.iWindowZoom; + float soff = 0.5f; + #else + float zoom = 0.3f; /// g_Config.iWindowZoom; + float soff = 0.3f; + #endif + PPGeBegin(); + PPGeDrawText(stats, soff, soff, 0, zoom, 0xCC000000); + PPGeDrawText(stats, -soff, -soff, 0, zoom, 0xCC000000); + PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFFFFFFF); + PPGeEnd(); + + gpuStats.resetFrame(); + kernelStats.ResetFrame(); +} + +// Let's collect all the throttling and frameskipping logic here. +void DoFrameTiming(bool &throttle, bool &skipFrame) { +#ifdef _WIN32 + throttle = !GetAsyncKeyState(VK_TAB); +#else + throttle = true; +#endif + skipFrame = false; + if (PSP_CoreParameter().headLess) + throttle = false; + + // Check if the frameskipping code should be enabled. If neither throttling or frameskipping is on, + // we have nothing to do here. + bool doFrameSkip = g_Config.iFrameSkip == 1; + if (!throttle && !doFrameSkip) + return; + + time_update(); + + curFrameTime = time_now_d(); + if (nextFrameTime == 0.0) + nextFrameTime = time_now_d() + 1.0 / 60.0; + + if (curFrameTime > nextFrameTime && doFrameSkip) { + // Argh, we are falling behind! Let's skip a frame and see if we catch up. + skipFrame = true; + // INFO_LOG(HLE,"FRAMESKIP %i", numSkippedFrames); + } + + if (curFrameTime < nextFrameTime && throttle) + { + // If time gap is huge just jump (somebody unthrottled) + if (nextFrameTime - curFrameTime > 1.0 / 30.0) { + nextFrameTime = curFrameTime + 1.0 / 60.0; + } else { + // Wait until we've catched up. + while (time_now_d() < nextFrameTime) { + Common::SleepCurrentThread(1); + time_update(); + } + } + curFrameTime = time_now_d(); + } + // Advance lastFrameTime by a constant amount each frame, + // but don't let it get too far behind as things can get very jumpy. + const double maxFallBehindFrames = 5.5; + + if (throttle || doFrameSkip) { + nextFrameTime = std::max(nextFrameTime + 1.0 / 60.0, time_now_d() - maxFallBehindFrames / 60.0); + } else { + nextFrameTime = nextFrameTime + 1.0 / 60.0; + } + + // Max 4 skipped frames in a row - 15 fps is really the bare minimum for playability. + // We check for 3 here so it's 3 skipped frames, 1 non skipped, 3 skipped, etc. + if (numSkippedFrames >= 3) { + skipFrame = false; + } } + void hleEnterVblank(u64 userdata, int cyclesLate) { int vbCount = userdata; @@ -223,7 +380,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { // Trigger VBlank interrupt handlers. __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL); - CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1); + CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1); // TODO: Should this be done here or in hleLeaveVblank? if (framebufIsLatched) { @@ -233,121 +390,63 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); } - // Draw screen overlays before blitting. Saves and restores the Ge context. gpuStats.numFrames++; // Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc. if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) { - gpu->UpdateStats(); - char stats[2048]; - - sprintf(stats, - "Frames: %i\n" - "DL processing time: %0.2f ms\n" - "Kernel processing time: %0.2f ms\n" - "Slowest syscall: %s : %0.2f ms\n" - "Most active syscall: %s : %0.2f ms\n" - "Draw calls: %i, flushes %i\n" - "Cached Draw calls: %i\n" - "Num Tracked Vertex Arrays: %i\n" - "Vertices Submitted: %i\n" - "Cached Vertices Drawn: %i\n" - "Uncached Vertices Drawn: %i\n" - "FBOs active: %i\n" - "Textures active: %i, decoded: %i\n" - "Texture invalidations: %i\n" - "Vertex shaders loaded: %i\n" - "Fragment shaders loaded: %i\n" - "Combined shaders loaded: %i\n", - gpuStats.numFrames, - gpuStats.msProcessingDisplayLists * 1000.0f, - kernelStats.msInSyscalls * 1000.0f, - kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", - kernelStats.slowestSyscallTime * 1000.0f, - kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", - kernelStats.summedSlowestSyscallTime * 1000.0f, - gpuStats.numDrawCalls, - gpuStats.numFlushes, - gpuStats.numCachedDrawCalls, - gpuStats.numTrackedVertexArrays, - gpuStats.numVertsSubmitted, - gpuStats.numCachedVertsDrawn, - gpuStats.numUncachedVertsDrawn, - gpuStats.numFBOs, - gpuStats.numTextures, - gpuStats.numTexturesDecoded, - gpuStats.numTextureInvalidations, - gpuStats.numVertexShaders, - gpuStats.numFragmentShaders, - gpuStats.numShaders - ); - - float zoom = 0.3f; /// g_Config.iWindowZoom; - float soff = 0.3f; - PPGeBegin(); - PPGeDrawText(stats, soff, soff, 0, zoom, 0xCC000000); - PPGeDrawText(stats, -soff, -soff, 0, zoom, 0xCC000000); - PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFFFFFFF); - PPGeEnd(); - - gpuStats.resetFrame(); - kernelStats.ResetFrame(); + DebugStats(); } if (g_Config.bShowFPSCounter) { - char stats[50]; - - sprintf(stats, "%0.1f", calculateFPS()); - - #ifdef USING_GLES2 - float zoom = 0.7f; /// g_Config.iWindowZoom; - float soff = 0.7f; - #else - float zoom = 0.5f; /// g_Config.iWindowZoom; - float soff = 0.5f; - #endif - PPGeBegin(); - PPGeDrawText(stats, 476 + soff, 4 + soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000); - PPGeDrawText(stats, 476 + -soff, 4 -soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000); - PPGeDrawText(stats, 476, 4, PPGE_ALIGN_RIGHT, zoom, 0xFF30FF30); - PPGeEnd(); + CalculateFPS(); } + bool skipFlip = false; + + // This frame was skipped, so no need to flip. + if (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) { + skipFlip = true; + } + + // Draw screen overlays before blitting. Saves and restores the Ge context. // Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity // to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have // anything to draw here. - gpu->CopyDisplayToOutput(); + gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME; - host->EndFrame(); + bool throttle, skipFrame; + + DoFrameTiming(throttle, skipFrame); -#ifdef _WIN32 - // Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it. - time_update(); - if (lastFrameTime == 0.0) - lastFrameTime = time_now_d(); - if (!GetAsyncKeyState(VK_TAB) && !PSP_CoreParameter().headLess) { - while (time_now_d() < lastFrameTime + 1.0 / 60.0) { - Common::SleepCurrentThread(1); - time_update(); - } - // Advance lastFrameTime by a constant amount each frame, - // but don't let it get too far behind. - lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0); + if (skipFrame) { + gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME; + numSkippedFrames++; + } else { + numSkippedFrames = 0; } - // We are going to have to do something about audio timing for platforms that - // are vsynced to something that's not exactly 60fps.. + if (!skipFlip) { + // Setting CORE_NEXTFRAME causes a swap. + // Check first though, might've just quit / been paused. + if (coreState == CORE_RUNNING && gpu->FramebufferDirty()) { + coreState = CORE_NEXTFRAME; + } -#endif + gpu->CopyDisplayToOutput(); + } + + // Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame). + // Right after, we regain control for a little bit in hleAfterFlip. I think that's a great + // place to do housekeeping. + CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0); +} +void hleAfterFlip(u64 userdata, int cyclesLate) +{ + // This checks input on PC. Fine to do even if not calling BeginFrame. host->BeginFrame(); - gpu->BeginFrame(); - // Tell the emu core that it's time to stop emulating - // Win32 doesn't need this. -#ifndef _WIN32 - coreState = CORE_NEXTFRAME; -#endif + gpu->BeginFrame(); // doesn't really matter if begin or end of frame. } void hleLeaveVblank(u64 userdata, int cyclesLate) { @@ -358,31 +457,28 @@ void hleLeaveVblank(u64 userdata, int cyclesLate) { CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata); } -void sceDisplayIsVblank() { +u32 sceDisplayIsVblank() { DEBUG_LOG(HLE,"%i=sceDisplayIsVblank()",isVblank); - RETURN(isVblank); + return isVblank; } -u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres) { - DEBUG_LOG(HLE,"sceDisplaySetMode(%d,%d,%d)",unknown,xres,yres); +u32 sceDisplaySetMode(int displayMode, int displayWidth, int displayHeight) { + DEBUG_LOG(HLE,"sceDisplaySetMode(%i, %i, %i)", displayMode, displayWidth, displayHeight); host->BeginFrame(); if (!hasSetMode) { gpu->InitClear(); hasSetMode = true; } - + mode = displayMode; + width = displayWidth; + height = displayHeight; return 0; } -u32 sceDisplaySetFramebuf() { - u32 topaddr = PARAM(0); - int linesize = PARAM(1); - int pixelformat = PARAM(2); - int sync = PARAM(3); - +u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) { FrameBufferState fbstate; - DEBUG_LOG(HLE,"sceDisplaySetFramebuf(topaddr=%08x,linesize=%d,pixelsize=%d,sync=%d)",topaddr,linesize,pixelformat,sync); + DEBUG_LOG(HLE,"sceDisplaySetFramebuf(topaddr=%08x,linesize=%d,pixelsize=%d,sync=%d)", topaddr, linesize, pixelformat, sync); if (topaddr == 0) { DEBUG_LOG(HLE,"- screen off"); } else { @@ -422,8 +518,7 @@ bool __DisplayGetFramebuf(u8 **topaddr, u32 *linesize, u32 *pixelFormat, int mod u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) { const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf; - DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)", - topaddrPtr, fbState.topaddr, linesizePtr, fbState.pspFramebufLinesize, pixelFormatPtr, fbState.pspFramebufFormat, mode); + DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)", topaddrPtr, fbState.topaddr, linesizePtr, fbState.pspFramebufLinesize, pixelFormatPtr, fbState.pspFramebufFormat, mode); if (Memory::IsValidAddress(topaddrPtr)) Memory::Write_U32(fbState.topaddr, topaddrPtr); @@ -500,15 +595,21 @@ u32 sceDisplayGetVcount() { return vCount; } -void sceDisplayGetCurrentHcount() { - RETURN(hCount++); +u32 sceDisplayGetCurrentHcount() { + ERROR_LOG(HLE,"sceDisplayGetCurrentHcount()"); + return hCount++; +} + +u32 sceDisplayAdjustAccumulatedHcount() { + ERROR_LOG(HLE,"UNIMPL sceDisplayAdjustAccumulatedHcount()"); + return 0; } -void sceDisplayGetAccumulatedHcount() { +u32 sceDisplayGetAccumulatedHcount() { // Just do an estimate u32 accumHCount = CoreTiming::GetTicks() / (CoreTiming::GetClockFrequencyMHz() * 1000000 / 60 / 272); DEBUG_LOG(HLE,"%i=sceDisplayGetAccumulatedHcount()", accumHCount); - RETURN(accumHCount); + return accumHCount; } float sceDisplayGetFramePerSec() { @@ -517,9 +618,25 @@ float sceDisplayGetFramePerSec() { return fps; // (9MHz * 1)/(525 * 286) } +u32 sceDisplayIsForeground() { + ERROR_LOG(HLE,"UNIMPL sceDisplayIsForeground()"); + return 1; // return value according to JPCSP comment +} + +u32 sceDisplayGetMode(u32 modeAddr, u32 widthAddr, u32 heightAddr) { + DEBUG_LOG(HLE,"sceDisplayGetMode()", modeAddr, widthAddr, heightAddr); + if (Memory::IsValidAddress(modeAddr)) + Memory::Write_U32(mode, modeAddr); + if (Memory::IsValidAddress(widthAddr)) + Memory::Write_U32(width, widthAddr); + if (Memory::IsValidAddress(heightAddr)) + Memory::Write_U32(height, heightAddr); + return 0; +} + const HLEFunction sceDisplay[] = { - {0x0E20F177,WrapU_UUU, "sceDisplaySetMode"}, - {0x289D82FE,WrapU_V, "sceDisplaySetFramebuf"}, + {0x0E20F177,WrapU_III, "sceDisplaySetMode"}, + {0x289D82FE,WrapU_UIII, "sceDisplaySetFramebuf"}, {0xEEDA2E54,WrapU_UUUI,"sceDisplayGetFrameBuf"}, {0x36CDFADE,WrapU_V, "sceDisplayWaitVblank"}, {0x984C27E7,WrapU_V, "sceDisplayWaitVblankStart"}, @@ -528,17 +645,17 @@ const HLEFunction sceDisplay[] = { {0x46F186C3,WrapU_V, "sceDisplayWaitVblankStartCB"}, {0x77ed8b3a,WrapU_I,"sceDisplayWaitVblankStartMultiCB"}, {0xdba6c4c4,WrapF_V,"sceDisplayGetFramePerSec"}, - {0x773dd3a3,sceDisplayGetCurrentHcount,"sceDisplayGetCurrentHcount"}, - {0x210eab3a,sceDisplayGetAccumulatedHcount,"sceDisplayGetAccumulatedHcount"}, - {0xA83EF139,0,"sceDisplayAdjustAccumulatedHcount"}, + {0x773dd3a3,WrapU_V,"sceDisplayGetCurrentHcount"}, + {0x210eab3a,WrapU_V,"sceDisplayGetAccumulatedHcount"}, + {0xA83EF139,WrapU_V,"sceDisplayAdjustAccumulatedHcount"}, {0x9C6EAAD7,WrapU_V,"sceDisplayGetVcount"}, - {0xDEA197D4,0,"sceDisplayGetMode"}, + {0xDEA197D4,WrapU_UUU,"sceDisplayGetMode"}, {0x7ED59BC4,0,"sceDisplaySetHoldMode"}, {0xA544C486,0,"sceDisplaySetResumeMode"}, {0xBF79F646,0,"sceDisplayGetResumeMode"}, - {0xB4F378FA,0,"sceDisplayIsForeground"}, + {0xB4F378FA,WrapU_V,"sceDisplayIsForeground"}, {0x31C4BAA8,0,"sceDisplayGetBrightness"}, - {0x4D4E10EC,sceDisplayIsVblank,"sceDisplayIsVblank"}, + {0x4D4E10EC,WrapU_V,"sceDisplayIsVblank"}, {0x21038913,0,"sceDisplayIsVsync"}, }; diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index 867d7ea664d1..9c027745b930 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -2,9 +2,41 @@ #include "base/timeutil.h" +#include +#include +#include +#include + #include "HLE.h" #include "../MIPS/MIPS.h" #include "ChunkFile.h" +#include "Core/FileSystems/FileSystem.h" +#include "Core/System.h" +#include "Core/HLE/sceKernel.h" +#include "Core/Font/PGF.h" +#include "Core/HLE/sceKernelThread.h" + +enum { + ERROR_FONT_INVALID_LIBID = 0x80460002, + ERROR_FONT_INVALID_PARAMETER = 0x80460003, + ERROR_FONT_TOO_MANY_OPEN_FONTS = 0x80460009, +}; + +enum { + FONT_IS_CLOSED = 0, + FONT_IS_OPEN = 1, +}; + +// Actions +static int actionPostAllocCallback; +static int actionPostOpenCallback; + +// Monster Hunter sequence: +// 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:469 E[HLE]: sceFontNewLib 89ad4a0, 9fff5cc +// 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:699 E[HLE]: UNIMPL sceFontGetNumFontList 1, 9fff5cc +// 36:46:998 c:\dev\ppsspp\core\hle\scefont.cpp:526 E[HLE]: sceFontFindOptimumFont 1, 9fff524, 9fff5cc +// 36:46:999 c:\dev\ppsspp\core\hle\scefont.cpp:490 E[HLE]: sceFontOpenFont 1, 1, 0, 9fff5cc +// 36:46:999 c:\dev\ppsspp\core\hle\scefont.cpp:542 E[HLE]: sceFontGetFontInfo 1, 997140c typedef u32 FontLibraryHandle; typedef u32 FontHandle; @@ -25,298 +57,735 @@ struct FontNewLibParams { u32 ioFinishFuncAddr; }; -typedef enum Family { - FONT_FAMILY_SANS_SERIF = 1, - FONT_FAMILY_SERIF = 2, +struct GlyphImage { + FontPixelFormat pixelFormat; + s32 xPos64; + s32 yPos64; + u16 bufWidth; + u16 bufHeight; + u16 bytesPerLine; + u16 pad; + u32 bufferPtr; }; -typedef enum Style { - FONT_STYLE_REGULAR = 1, - FONT_STYLE_ITALIC = 2, - FONT_STYLE_BOLD = 5, - FONT_STYLE_BOLD_ITALIC = 6, - FONT_STYLE_DB = 103, // Demi-Bold / semi-bold +struct FontRegistryEntry { + int hSize; + int vSize; + int hResolution; + int vResolution; + int extraAttributes; + int weight; + int familyCode; + int style; + int styleSub; + int languageCode; + int regionCode; + int countryCode; + const char *fileName; + const char *fontName; + int expireDate; + int shadow_option; }; -typedef enum Language { - FONT_LANGUAGE_JAPANESE = 1, - FONT_LANGUAGE_LATIN = 2, - FONT_LANGUAGE_KOREAN = 3, +static const FontRegistryEntry fontRegistry[] = { + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_DB, 0, FONT_LANGUAGE_JAPANESE, 0, 1, "jpn0.pgf", "FTT-NewRodin Pro DB", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn0.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn1.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn2.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn3.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn4.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn5.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn6.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn7.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn8.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn9.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn10.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn11.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn12.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn13.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn14.pgf", "FTT-NewRodin Pro Latin", 0, 0}, + {0x1c0, 0x1c0, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SERIF, FONT_STYLE_BOLD_ITALIC, 0, FONT_LANGUAGE_LATIN, 0, 1, "ltn15.pgf", "FTT-Matisse Pro Latin", 0, 0}, + {0x288, 0x288, 0x2000, 0x2000, 0, 0, FONT_FAMILY_SANS_SERIF, FONT_STYLE_REGULAR, 0, FONT_LANGUAGE_KOREAN, 0, 3, "kr0.pgf", "AsiaNHH(512Johab)", 0, 0}, }; -struct FontStyle { - float fontH; - float fontV; - float fontHRes; - float fontVRes; - float fontWeight; - u16 fontFamily; - u16 fontStyle; - // Check. - u16 fontStyleSub; - u16 fontLanguage; - u16 fontRegion; - u16 fontCountry; - char fontName[64]; - char fontFileName[64]; - u32 fontAttributes; - u32 fontExpire; +static const float pointDPI = 72.f; + +class LoadedFont; +class FontLib; +class Font; +int GetInternalFontIndex(Font *font); + +// These should not need to be state saved. +static std::vector internalFonts; +// However, these we must save - but we could take a shortcut +// for LoadedFonts that point to internal fonts. +static std::map fontMap; +static std::map fontLibMap; +// We keep this list to avoid ptr references, even before alloc is called. +static std::vector fontLibList; + +// TODO: Merge this class with PGF? That'd make it harder to support .bwfon +// fonts though, unless that's added directly to PGF. +class Font { +public: + // For savestates only. + Font() { + } + + Font(const u8 *data, size_t dataSize) { + pgf_.ReadPtr(data, dataSize); + style_.fontH = pgf_.header.hSize / 64.0f; + style_.fontV = pgf_.header.vSize / 64.0f; + style_.fontHRes = pgf_.header.hResolution / 64.0f; + style_.fontVRes = pgf_.header.vResolution / 64.0f; + } + + Font(const u8 *data, size_t dataSize, const FontRegistryEntry &entry) { + pgf_.ReadPtr(data, dataSize); + style_.fontH = entry.hSize / 64.f; + style_.fontV = entry.vSize / 64.f; + style_.fontHRes = entry.hResolution / 64.f; + style_.fontVRes = entry.vResolution / 64.f; + style_.fontWeight = (float)entry.weight; + style_.fontFamily = (u16)entry.familyCode; + style_.fontStyle = (u16)entry.style; + style_.fontStyleSub = (u16)entry.styleSub; + style_.fontLanguage = (u16)entry.languageCode; + style_.fontRegion = (u16)entry.regionCode; + style_.fontCountry = (u16)entry.countryCode; + strncpy(style_.fontName, entry.fontName, sizeof(style_.fontName)); + strncpy(style_.fontFileName, entry.fileName, sizeof(style_.fontFileName)); + style_.fontAttributes = entry.extraAttributes; + style_.fontExpire = entry.expireDate; + } + + const PGFFontStyle &GetFontStyle() const { return style_; } + + bool MatchesStyle(const PGFFontStyle &style, bool optimum) const { + // TODO + return true; + } + + PGF *GetPGF() { return &pgf_; } + + void DoState(PointerWrap &p) { + p.Do(pgf_); + p.Do(style_); + p.DoMarker("Font"); + } + +private: + PGF pgf_; + PGFFontStyle style_; + DISALLOW_COPY_AND_ASSIGN(Font); }; -struct FontInfo { - // Glyph metrics (in 26.6 signed fixed-point). - u32 maxGlyphWidthI; - u32 maxGlyphHeightI; - u32 maxGlyphAscenderI; - u32 maxGlyphDescenderI; - u32 maxGlyphLeftXI; - u32 maxGlyphBaseYI; - u32 minGlyphCenterXI; - u32 maxGlyphTopYI; - u32 maxGlyphAdvanceXI; - u32 maxGlyphAdvanceYI; - - // Glyph metrics (replicated as float). - float maxGlyphWidthF; - float maxGlyphHeightF; - float maxGlyphAscenderF; - float maxGlyphDescenderF; - float maxGlyphLeftXF; - float maxGlyphBaseYF; - float minGlyphCenterXF; - float maxGlyphTopYF; - float maxGlyphAdvanceXF; - float maxGlyphAdvanceYF; - - // Bitmap dimensions. - short maxGlyphWidth; - short maxGlyphHeight; - u32 charMapLength; // Number of elements in the font's charmap. - u32 shadowMapLength; // Number of elements in the font's shadow charmap. - - // Font style (used by font comparison functions). - FontStyle fontStyle; - - u8 BPP; // Font's BPP. - u8 pad[3]; +class LoadedFont { +public: + // For savestates only. + LoadedFont() { + } + + LoadedFont(Font *font, u32 fontLibID, u32 handle) + : fontLibID_(fontLibID), font_(font), handle_(handle) {} + + Font *GetFont() { return font_; } + FontLib *GetFontLib() { return fontLibList[fontLibID_]; } + u32 Handle() const { return handle_; } + + bool IsOpen() const { return fontLibID_ != (u32)-1; } + void Close() { + fontLibID_ = (u32)-1; + // We keep the rest around until deleted, as some queries are allowed + // on closed fonts (which is rather strange). + } + + void DoState(PointerWrap &p) { + int numInternalFonts = (int)internalFonts.size(); + p.Do(numInternalFonts); + if (numInternalFonts != (int)internalFonts.size()) { + ERROR_LOG(HLE, "Unable to load state: different internal font count."); + return; + } + + p.Do(fontLibID_); + int internalFont = GetInternalFontIndex(font_); + p.Do(internalFont); + if (internalFont == -1) { + p.Do(font_); + } else if (p.mode == p.MODE_READ) { + font_ = internalFonts[internalFont]; + } + p.Do(handle_); + p.DoMarker("LoadedFont"); + } + +private: + u32 fontLibID_; + Font *font_; + u32 handle_; + DISALLOW_COPY_AND_ASSIGN(LoadedFont); }; -struct CharInfo { - u32 bitmapWidth; - u32 bitmapHeight; - u32 bitmapLeft; - u32 bitmapTop; - // Glyph metrics (in 26.6 signed fixed-point). - u32 spf26Width; - u32 spf26Height; - s32 spf26Ascender; - s32 spf26Descender; - s32 spf26BearingHX; - s32 spf26BearingHY; - s32 spf26BearingVX; - s32 spf26BearingVY; - s32 spf26AdvanceH; - s32 spf26AdvanceV; - u8 pad[4]; +class PostAllocCallback : public Action { +public: + PostAllocCallback() {} + static Action *Create() { return new PostAllocCallback(); } + void DoState(PointerWrap &p) { p.Do(fontLibID_); p.DoMarker("PostAllocCallback"); } + void run(MipsCall &call); + void SetFontLib(u32 fontLibID) { fontLibID_ = fontLibID; } + +private: + u32 fontLibID_; }; -enum FontPixelFormat { - PSP_FONT_PIXELFORMAT_4 = 0, - PSP_FONT_PIXELFORMAT_4_REV = 1, - PSP_FONT_PIXELFORMAT_8 = 2, - PSP_FONT_PIXELFORMAT_24 = 3, - PSP_FONT_PIXELFORMAT_32 = 4 +class PostOpenCallback : public Action { +public: + PostOpenCallback() {} + static Action *Create() { return new PostOpenCallback(); } + void DoState(PointerWrap &p) { p.Do(fontLibID_); p.DoMarker("PostOpenCallback"); } + void run(MipsCall &call); + void SetFontLib(u32 fontLibID) { fontLibID_ = fontLibID; } + +private: + u32 fontLibID_; }; -struct GlyphImage { - FontPixelFormat pixelFormat; - s32 xPos64; - s32 yPos64; - u16 bufWidth; - u16 bufHeight; - u16 bytesPerLine; - u16 pad; - u32 bufferPtr; +// A "fontLib" is a container of loaded fonts. +// One can open either "internal" fonts or custom fonts into a fontlib. +class FontLib { +public: + FontLib() { + // For save states only. + } + + FontLib(u32 paramPtr) : fontHRes_(128.0f), fontVRes_(128.0f) { + Memory::ReadStruct(paramPtr, ¶ms_); + fakeAlloc_ = 0x13370; + // We use the same strange scheme that JPCSP uses. + u32 allocSize = 4 + 4 * params_.numFonts; + PostAllocCallback *action = (PostAllocCallback*) __KernelCreateAction(actionPostAllocCallback); + action->SetFontLib(GetListID()); + + if (false) { + // This fails in dissidia. The function at 088ff320 (params_.allocFuncAddr) calls some malloc function, the second one returns 0 which causes + // bad stores later. So we just ignore this with if (false) and fake it. + u32 args[1] = { allocSize }; + __KernelDirectMipsCall(params_.allocFuncAddr, action, args, 1, false); + } else { + AllocDone(fakeAlloc_); + fakeAlloc_ += allocSize; + fontLibMap[handle()] = GetListID(); + INFO_LOG(HLE, "Leaving PostAllocCallback::run"); + } + } + + u32 GetListID() { + return (u32)(std::find(fontLibList.begin(), fontLibList.end(), this) - fontLibList.begin()); + } + + void Close() { + __KernelDirectMipsCall(params_.closeFuncAddr, 0, 0, 0, false); + } + + void Done() { + for (size_t i = 0; i < fonts_.size(); i++) { + if (isfontopen_[i] == FONT_IS_OPEN) { + fontMap[fonts_[i]]->Close(); + delete fontMap[fonts_[i]]; + fontMap.erase(fonts_[i]); + } + } + u32 args[1] = { (u32)handle_ }; + __KernelDirectMipsCall(params_.freeFuncAddr, 0, args, 1, false); + handle_ = 0; + fonts_.clear(); + isfontopen_.clear(); + } + + void AllocDone(u32 allocatedAddr) { + handle_ = allocatedAddr; + fonts_.resize(params_.numFonts); + isfontopen_.resize(params_.numFonts); + for (size_t i = 0; i < fonts_.size(); i++) { + u32 addr = allocatedAddr + 4 + i * 4; + isfontopen_[i] = 0; + fonts_[i] = addr; + } + } + + u32 handle() const { return handle_; } + int numFonts() const { return params_.numFonts; } + + void SetResolution(float hres, float vres) { + fontHRes_ = hres; + fontVRes_ = vres; + } + + float FontHRes() const { return fontHRes_; } + float FontVRes() const { return fontVRes_; } + + void SetAltCharCode(int charCode) { altCharCode_ = charCode; } + + int GetFontHandle(int index) { + return fonts_[index]; + } + + LoadedFont *OpenFont(Font *font) { + int freeFontIndex = -1; + for (size_t i = 0; i < fonts_.size(); i++) { + if (isfontopen_[i] == 0) { + freeFontIndex = (int)i; + break; + } + } + if (freeFontIndex < 0) { + ERROR_LOG(HLE, "Too many fonts opened in FontLib"); + return 0; + } + LoadedFont *loadedFont = new LoadedFont(font, GetListID(), fonts_[freeFontIndex]); + isfontopen_[freeFontIndex] = 1; + return loadedFont; + } + + void CloseFont(LoadedFont *font) { + for (size_t i = 0; i < fonts_.size(); i++) { + if (fonts_[i] == font->Handle()) { + isfontopen_[i] = 0; + + } + } + font->Close(); + } + + void DoState(PointerWrap &p) { + p.Do(fonts_); + p.Do(isfontopen_); + p.Do(params_); + p.Do(fontHRes_); + p.Do(fontVRes_); + p.Do(fileFontHandle_); + p.Do(handle_); + p.Do(altCharCode_); + p.Do(fakeAlloc_); + p.DoMarker("FontLib"); + } + + void SetFileFontHandle(u32 handle) { + fileFontHandle_ = handle; + } + + u32 GetAltCharCode() const { return altCharCode_; } + +private: + std::vector fonts_; + std::vector isfontopen_; + + FontNewLibParams params_; + float fontHRes_; + float fontVRes_; + int fileFontHandle_; + int handle_; + int altCharCode_; + + u32 fakeAlloc_; + DISALLOW_COPY_AND_ASSIGN(FontLib); }; -FontNewLibParams fontLib; -void __FontInit() -{ - memset(&fontLib, 0, sizeof(fontLib)); +void PostAllocCallback::run(MipsCall &call) { + INFO_LOG(HLE, "Entering PostAllocCallback::run"); + u32 v0 = currentMIPS->r[0]; + FontLib *fontLib = fontLibList[fontLibID_]; + fontLib->AllocDone(call.savedV0); + fontLibMap[fontLib->handle()] = fontLibID_; + call.setReturnValue(fontLib->handle()); + INFO_LOG(HLE, "Leaving PostAllocCallback::run"); +} + +void PostOpenCallback::run(MipsCall &call) { + FontLib *fontLib = fontLibList[fontLibID_]; + fontLib->SetFileFontHandle(call.savedV0); +} + +FontLib *GetFontLib(u32 handle) { + if (fontLibMap.find(handle) != fontLibMap.end()) { + return fontLibList[fontLibMap[handle]]; + } else { + ERROR_LOG(HLE, "No fontlib with handle %08x", handle); + return 0; + } +} + +LoadedFont *GetLoadedFont(u32 handle, bool allowClosed) { + auto iter = fontMap.find(handle); + if (iter != fontMap.end()) { + if (iter->second->IsOpen() || allowClosed) { + return fontMap[handle]; + } else { + ERROR_LOG(HLE, "Font exists but is closed, which was not allowed in this call."); + return 0; + } + } else { + ERROR_LOG(HLE, "No font with handle %08x", handle); + return 0; + } +} + +void __LoadInternalFonts() { + std::string fontPath = "flash0:/font/"; + if (!pspFileSystem.GetFileInfo(fontPath).exists) { + pspFileSystem.MkDir(fontPath); + } + for (size_t i = 0; i < ARRAY_SIZE(fontRegistry); i++) { + const FontRegistryEntry &entry = fontRegistry[i]; + std::string fontFilename = fontPath + entry.fileName; + PSPFileInfo info = pspFileSystem.GetFileInfo(fontFilename); + if (info.exists) { + INFO_LOG(HLE, "Loading font %s (%i bytes)", fontFilename.c_str(), (int)info.size); + u8 *buffer = new u8[(size_t)info.size]; + u32 handle = pspFileSystem.OpenFile(fontFilename, FILEACCESS_READ); + if (!handle) { + ERROR_LOG(HLE, "Failed opening font"); + delete [] buffer; + continue; + } + pspFileSystem.ReadFile(handle, buffer, info.size); + pspFileSystem.CloseFile(handle); + + internalFonts.push_back(new Font(buffer, (size_t)info.size, entry)); + + delete [] buffer; + INFO_LOG(HLE, "Loaded font %s", fontFilename.c_str()); + } else { + INFO_LOG(HLE, "Font file not found: %s", fontFilename.c_str()); + } + } } -void __FontDoState(PointerWrap &p) -{ - p.Do(fontLib); +Style FontStyleFromString(const std::string &str) { + if (str == "Regular") + return FONT_STYLE_REGULAR; + else if (str == "Italic") + return FONT_STYLE_ITALIC; + else if (str == "Bold") + return FONT_STYLE_BOLD; + else if (str == "Bold Italic") + return FONT_STYLE_BOLD_ITALIC; + return FONT_STYLE_REGULAR; +} + +Font *GetOptimumFont(const PGFFontStyle &requestedStyle, Font *optimumFont, Font *candidateFont) { + if (!optimumFont) + return candidateFont; + PGFFontStyle optimumStyle = optimumFont->GetFontStyle(); + PGFFontStyle candidateStyle = candidateFont->GetFontStyle(); + + bool testH = requestedStyle.fontH != 0.0f || requestedStyle.fontV == 0.0f; + if (testH && fabsf(requestedStyle.fontH - optimumStyle.fontH) > fabsf(requestedStyle.fontH - candidateStyle.fontH)) { + return candidateFont; + } + + // Check the fontV if it is specified or both fontH and fontV are unspecified + bool testV = requestedStyle.fontV != 0.f || requestedStyle.fontH == 0.f; + if (testV && fabsf(requestedStyle.fontV - optimumStyle.fontV) > fabsf(requestedStyle.fontV - candidateStyle.fontV)) { + return candidateFont; + } + + return optimumFont; +} + +int GetInternalFontIndex(Font *font) { + for (size_t i = 0; i < internalFonts.size(); i++) { + if (internalFonts[i] == font) + return i; + } + return -1; +} + +void __FontInit() { + __LoadInternalFonts(); + actionPostAllocCallback = __KernelRegisterActionType(PostAllocCallback::Create); + actionPostOpenCallback = __KernelRegisterActionType(PostOpenCallback::Create); +} + +void __FontShutdown() { + for (auto iter = fontMap.begin(); iter != fontMap.end(); iter++) { + FontLib *fontLib = iter->second->GetFontLib(); + if (fontLib) + fontLib->CloseFont(iter->second); + } + fontMap.clear(); + for (auto iter = fontLibList.begin(); iter != fontLibList.end(); iter++) { + delete *iter; + } + fontLibList.clear(); + fontLibMap.clear(); + for (auto iter = internalFonts.begin(); iter != internalFonts.end(); ++iter) { + delete *iter; + } + internalFonts.clear(); +} + +void __FontDoState(PointerWrap &p) { + p.Do(fontLibList); + p.Do(fontLibMap); + p.Do(fontMap); + + p.Do(actionPostAllocCallback); + __KernelRestoreActionType(actionPostAllocCallback, PostAllocCallback::Create); + p.Do(actionPostOpenCallback); + __KernelRestoreActionType(actionPostOpenCallback, PostOpenCallback::Create); p.DoMarker("sceFont"); } -u32 sceFontNewLib(u32 FontNewLibParamsPtr, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "sceFontNewLib %x, %x", FontNewLibParamsPtr, errorCodePtr); +u32 sceFontNewLib(u32 paramPtr, u32 errorCodePtr) { + INFO_LOG(HLE, "sceFontNewLib(%08x, %08x)", paramPtr, errorCodePtr); - if (Memory::IsValidAddress(FontNewLibParamsPtr)&&Memory::IsValidAddress(errorCodePtr)) - { + if (Memory::IsValidAddress(paramPtr) && Memory::IsValidAddress(errorCodePtr)) { Memory::Write_U32(0, errorCodePtr); - Memory::ReadStruct(FontNewLibParamsPtr, &fontLib); + + FontLib *newLib = new FontLib(paramPtr); + fontLibList.push_back(newLib); + // The game should never see this value, the return value is replaced + // by the action. Except if we disable the alloc, in this case we return + // the handle correctly here. + return newLib->handle(); } - return 1; + return 0; } - -int sceFontDoneLib(u32 FontLibraryHandlePtr) -{ - ERROR_LOG(HLE, "sceFontDoneLib %x", FontLibraryHandlePtr); +int sceFontDoneLib(u32 fontLibHandle) { + INFO_LOG(HLE, "sceFontDoneLib(%08x)", fontLibHandle); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + fl->Done(); + } return 0; } +// Open internal font into a FontLib +u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr) { + INFO_LOG(HLE, "sceFontOpen(%x, %x, %x, %x)", libHandle, index, mode, errorCodePtr); + if (!Memory::IsValidAddress(errorCodePtr)) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } -u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "sceFontDoneLib %x, %x, %x, %x", libHandle, index, mode, errorCodePtr); - if (Memory::IsValidAddress(errorCodePtr)) - { + FontLib *fontLib = GetFontLib(libHandle); + if (index >= internalFonts.size()) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + LoadedFont *font = fontLib->OpenFont(internalFonts[index]); + if (font) { + fontMap[font->Handle()] = font; Memory::Write_U32(0, errorCodePtr); + return font->Handle(); + } else { + Memory::Write_U32(ERROR_FONT_TOO_MANY_OPEN_FONTS, errorCodePtr); + return 0; } - return 1; } -u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontAddrPtr, u32 memoryFontLength, u32 errorCodePtr) -{ +// Open a user font in RAM into a FontLib +u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontAddrPtr, u32 memoryFontLength, u32 errorCodePtr) { ERROR_LOG(HLE, "sceFontOpenUserMemory %x, %x, %x, %x", libHandle, memoryFontAddrPtr, memoryFontLength, errorCodePtr); - if (Memory::IsValidAddress(errorCodePtr)) - { + if (!Memory::IsValidAddress(errorCodePtr) || !Memory::IsValidAddress(memoryFontAddrPtr)) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + FontLib *fontLib = GetFontLib(libHandle); + if (!fontLib) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + const u8 *fontData = Memory::GetPointer(memoryFontAddrPtr); + LoadedFont *font = fontLib->OpenFont(new Font(fontData, memoryFontLength)); + if (font) { + fontMap[font->Handle()] = font; Memory::Write_U32(0, errorCodePtr); + return font->Handle(); + } else { + Memory::Write_U32(ERROR_FONT_TOO_MANY_OPEN_FONTS, errorCodePtr); + return 0; } - return 1; } -u32 sceFontOpenUserFile(u32 libHandle, u32 fileNamePtr, u32 mode, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "sceFontOpenUserFile %x, %x, %x, %x", libHandle, fileNamePtr, mode, errorCodePtr); - if (Memory::IsValidAddress(errorCodePtr)) - { +// Open a user font in a file into a FontLib +u32 sceFontOpenUserFile(u32 libHandle, const char *fileName, u32 mode, u32 errorCodePtr) { + ERROR_LOG(HLE, "sceFontOpenUserFile(%08x, %s, %08x, %08x)", libHandle, fileName, mode, errorCodePtr); + if (!Memory::IsValidAddress(errorCodePtr)) + return ERROR_FONT_INVALID_PARAMETER; + + PSPFileInfo info = pspFileSystem.GetFileInfo(fileName); + if (!info.exists) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + FontLib *fontLib = GetFontLib(libHandle); + if (!fontLib) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + u8 *buffer = new u8[(size_t)info.size]; + + u32 fileHandle = pspFileSystem.OpenFile(fileName, FILEACCESS_READ); + pspFileSystem.ReadFile(fileHandle, buffer, info.size); + pspFileSystem.CloseFile(fileHandle); + + LoadedFont *font = fontLib->OpenFont(new Font(buffer, (size_t)info.size)); + if (font) { + fontMap[font->Handle()] = font; Memory::Write_U32(0, errorCodePtr); + return font->Handle(); + } else { + Memory::Write_U32(ERROR_FONT_TOO_MANY_OPEN_FONTS, errorCodePtr); + return 0; } - return 1; } -int sceFontClose(u32 fontHandle) -{ - ERROR_LOG(HLE, "sceFontClose %x", fontHandle); +int sceFontClose(u32 fontHandle) { + LoadedFont *font = GetLoadedFont(fontHandle, false); + if (font) + { + INFO_LOG(HLE, "sceFontClose(%x)", fontHandle); + FontLib *fontLib = font->GetFontLib(); + if (fontLib) + fontLib->CloseFont(font); + } + else + ERROR_LOG(HLE, "sceFontClose(%x) - font not open?", fontHandle); return 0; } -int sceFontFindOptimumFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "sceFontFindOptimumFont %x, %x, %x", libHandlePtr, fontStylePtr, errorCodePtr); - if (Memory::IsValidAddress(errorCodePtr)) - { +int sceFontFindOptimumFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr) { + ERROR_LOG(HLE, "sceFontFindOptimumFont(%08x, %08x, %08x)", libHandlePtr, fontStylePtr, errorCodePtr); + if (!fontStylePtr) + return 0; + + if (!Memory::IsValidAddress(errorCodePtr)) + return SCE_KERNEL_ERROR_INVALID_ARGUMENT; + + PGFFontStyle requestedStyle; + Memory::ReadStruct(fontStylePtr, &requestedStyle); + + Font *optimumFont = 0; + for (size_t i = 0; i < internalFonts.size(); i++) { + if (internalFonts[i]->MatchesStyle(requestedStyle, true)) { + optimumFont = GetOptimumFont(requestedStyle, optimumFont, internalFonts[i]); + break; + } + } + if (optimumFont) { Memory::Write_U32(0, errorCodePtr); + return GetInternalFontIndex(optimumFont); + } else { + Memory::Write_U32(0, errorCodePtr); + return 0; } - return 1; } -int sceFontFindFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "sceFontFindFont %x, %x, %x", libHandlePtr, fontStylePtr, errorCodePtr); - return 1; +// Returns the font index, not handle +int sceFontFindFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr) { + ERROR_LOG(HLE, "sceFontFindFont(%x, %x, %x)", libHandlePtr, fontStylePtr, errorCodePtr); + if (!Memory::IsValidAddress(errorCodePtr)) { + Memory::Write_U32(ERROR_FONT_INVALID_PARAMETER, errorCodePtr); + return 0; + } + + PGFFontStyle style; + Memory::ReadStruct(fontStylePtr, &style); + + for (size_t i = 0; i < internalFonts.size(); i++) { + if (internalFonts[i]->MatchesStyle(style, false)) { + Memory::Write_U32(0, errorCodePtr); + return i; + } + } + return -1; } -int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) -{ - ERROR_LOG(HLE, "sceFontGetFontInfo %x, %x", fontHandle, fontInfoPtr); +int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) { + ERROR_LOG(HLE, "sceFontGetFontInfo(%x, %x)", fontHandle, fontInfoPtr); - FontInfo fi; + PGFFontInfo fi; memset (&fi, 0, sizeof(fi)); - if (Memory::IsValidAddress(fontInfoPtr)) - { - fi.BPP = 4; - fi.charMapLength = 255; - fi.maxGlyphAdvanceXF = 2.0; - fi.maxGlyphAdvanceXI = 2; - fi.maxGlyphAdvanceYF = 2.0; - fi.maxGlyphAdvanceYI = 32 << 6; - fi.maxGlyphAscenderF = 32 << 6; - fi.maxGlyphAscenderI = 32 << 6; - fi.maxGlyphBaseYF = 0.0; - fi.maxGlyphBaseYI = 0; - fi.maxGlyphDescenderF = 0; - fi.maxGlyphDescenderI = 0; - fi.maxGlyphHeight = 32; - fi.maxGlyphHeightF = 32; - fi.maxGlyphHeightI = 32; - fi.maxGlyphLeftXF = 0; - fi.maxGlyphLeftXI = 0; - fi.maxGlyphTopYF = 0; - fi.maxGlyphTopYI = 0; - fi.maxGlyphWidth = 32; - fi.maxGlyphWidthF = 32; - fi.maxGlyphWidthI = 32; - fi.minGlyphCenterXF = 16; - fi.minGlyphCenterXI = 16; - fi.shadowMapLength = 0; - fi.fontStyle.fontAttributes=1; - fi.fontStyle.fontCountry= 1; - fi.fontStyle.fontExpire= 1; - fi.fontStyle.fontFamily= 1; - strcpy(fi.fontStyle.fontFileName, "asd"); - fi.fontStyle.fontH=32; - fi.fontStyle.fontHRes=32; - fi.fontStyle.fontLanguage=1; - strcpy(fi.fontStyle.fontName, "ppsspp"); - fi.fontStyle.fontRegion=9; - fi.fontStyle.fontV=32; - fi.fontStyle.fontVRes=32; - fi.fontStyle.fontWeight= 32; - Memory::WriteStruct(fontInfoPtr, &fi); - } + if (!Memory::IsValidAddress(fontInfoPtr)) + return 0; + + LoadedFont *font = GetLoadedFont(fontHandle, true); + if (!font) + return 0; + PGF *pgf = font->GetFont()->GetPGF(); + pgf->GetFontInfo(&fi); + fi.fontStyle = font->GetFont()->GetFontStyle(); + Memory::WriteStruct(fontInfoPtr, &fi); return 0; } -int sceFontGetFontInfoByIndexNumber(u32 libHandle, u32 fontInfoPtr, u32 unknown, u32 fontIndex) -{ - ERROR_LOG(HLE, "HACK sceFontGetFontInfoByIndexNumber %x, %x, %x, %x", libHandle, fontInfoPtr, unknown, fontIndex); - // clearly wrong.. - return sceFontGetFontInfo(libHandle, fontInfoPtr); +int sceFontGetFontInfoByIndexNumber(u32 libHandle, u32 fontInfoPtr, u32 unknown, u32 fontIndex) { + ERROR_LOG(HLE, "HACK sceFontGetFontInfoByIndexNumber(%x, %x, %i, %i)", libHandle, fontInfoPtr, unknown, fontIndex); + FontLib *fl = GetFontLib(libHandle); + u32 fontHandle = fl->GetFontHandle(fontIndex); + return sceFontGetFontInfo(fontHandle, fontInfoPtr); +} +int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) { + INFO_LOG(HLE, "sceFontGetCharInfo(%08x, %i, %08x)", fontHandle, charCode, charInfoPtr); + if (!Memory::IsValidAddress(charInfoPtr)) + return -1; + + PGFCharInfo charInfo; + memset(&charInfo, 0, sizeof(charInfo)); + LoadedFont *font = GetLoadedFont(fontHandle, false); + if (font) { + font->GetFont()->GetPGF()->GetCharInfo(charCode, &charInfo); + } else { + ERROR_LOG(HLE, "sceFontGetCharInfo - invalid font"); + } + Memory::WriteStruct(charInfoPtr, &charInfo); + return 0; } -int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) -{ - ERROR_LOG(HLE, "HACK sceFontGetCharInfo %x, %x, %x", fontHandle, charCode, charInfoPtr); - if (Memory::IsValidAddress(charInfoPtr)) - { - CharInfo pspCharInfo; - memset(&pspCharInfo, 0, sizeof(pspCharInfo)); - pspCharInfo.bitmapWidth = 16; - pspCharInfo.bitmapHeight = 16; +// Not sure about the arguments. +int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 shadowCharInfoPtr) { + ERROR_LOG(HLE, "UNIMPL sceFontGetShadowInfo(%08x, %i, %08x)", fontHandle, charCode, shadowCharInfoPtr); + // TODO + return 0; +} - pspCharInfo.spf26Width = pspCharInfo.bitmapWidth << 6; - pspCharInfo.spf26Height = pspCharInfo.bitmapHeight << 6; - pspCharInfo.spf26AdvanceH = pspCharInfo.bitmapWidth << 6; - pspCharInfo.spf26AdvanceV = pspCharInfo.bitmapHeight << 6; - Memory::WriteStruct(charInfoPtr, &pspCharInfo); +int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) { + ERROR_LOG(HLE, "HACK sceFontGetCharImageRect(%08x, %i, %08x) (char: %c)", fontHandle, charCode, charRectPtr, (char)charCode); + if (!Memory::IsValidAddress(charRectPtr)) + return -1; + + PGFCharInfo charInfo; + LoadedFont *font = GetLoadedFont(fontHandle, false); + if (font) { + font->GetFont()->GetPGF()->GetCharInfo(charCode, &charInfo); + Memory::Write_U16(charInfo.bitmapWidth, charRectPtr); // character bitmap width in pixels + Memory::Write_U16(charInfo.bitmapHeight, charRectPtr + 2); // character bitmap height in pixels + } else { + ERROR_LOG(HLE, "sceFontGetCharImageRect - invalid font"); } return 0; } -int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) -{ - ERROR_LOG(HLE, "HACK sceFontGetCharImageRect %x, %x (%c)", fontHandle, charRectPtr, charCode); - if (Memory::IsValidAddress(charRectPtr)) { - Memory::Write_U16(16, charRectPtr); // character bitmap width in pixels - Memory::Write_U16(16, charRectPtr + 2); // character bitmap height in pixels - } +int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) { + ERROR_LOG(HLE, "UNIMPL sceFontGetShadowImageRect()"); return 0; } -int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) -{ - ERROR_LOG(HLE, "HACK sceFontGetCharGlyphImage %x, %x, %x (%c)", fontHandle, charCode, glyphImagePtr, charCode); +int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) { + ERROR_LOG(HLE, "HACK sceFontGetCharGlyphImage(%x, %x, %x) (char: %c)", fontHandle, charCode, glyphImagePtr, (char)charCode); int pixelFormat = Memory::Read_U32(glyphImagePtr); int xPos64 = Memory::Read_U32(glyphImagePtr+4); @@ -326,74 +795,123 @@ int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) int bytesPerLine = Memory::Read_U16(glyphImagePtr+16); int buffer = Memory::Read_U32(glyphImagePtr+20); - // Small chessboard. Does not respect pixelformat currently... - - // Actually should be really easy to substitute in a proper font here... - // could even grab pixel data from the PPGe one. - for (int y = 0; y < bufHeight; y++) - { - for (int x = 0; x < bytesPerLine; x++) - { - Memory::Write_U8((((x >> 1) ^ (y >> 1)) & 1) ? 0xff : 0x00, buffer + (y * bytesPerLine + x)); - } + LoadedFont *font = GetLoadedFont(fontHandle, false); + if (!font) { + ERROR_LOG(HLE, "%08x is not a valid font handle!", fontHandle); + return 0; } + int altCharCode = font->GetFontLib()->GetAltCharCode(); + font->GetFont()->GetPGF()->DrawCharacter(buffer, bytesPerLine, bufWidth, bufHeight, xPos64 >> 6, yPos64 >> 6, 0, 0, 8192, 8192, pixelFormat, charCode, altCharCode, FONT_PGF_CHARGLYPH); + return 0; +} + +int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) { + ERROR_LOG(HLE, "sceFontGetCharGlyphImage_Clip(%08x, %i, %08x, %i, %i, %i, %i) (%c)", fontHandle, charCode, glyphImagePtr, clipXPos, clipYPos, clipWidth, clipHeight, charCode); + int pixelFormat = Memory::Read_U32(glyphImagePtr); + int xPos64 = Memory::Read_U32(glyphImagePtr+4); + int yPos64 = Memory::Read_U32(glyphImagePtr+8); + int bufWidth = Memory::Read_U16(glyphImagePtr+12); + int bufHeight = Memory::Read_U16(glyphImagePtr+14); + int bytesPerLine = Memory::Read_U16(glyphImagePtr+16); + int buffer = Memory::Read_U32(glyphImagePtr+20); + + LoadedFont *font = GetLoadedFont(fontHandle, false); + if (!font) { + ERROR_LOG(HLE, "%08x is not a valid font handle!", fontHandle); + return 0; + } + int altCharCode = font->GetFontLib()->GetAltCharCode(); + font->GetFont()->GetPGF()->DrawCharacter(buffer, bytesPerLine, bufWidth, bufHeight, xPos64 >> 6, yPos64 >> 6, clipXPos, clipYPos, clipXPos + clipWidth, clipYPos + clipHeight, pixelFormat, charCode, altCharCode, FONT_PGF_CHARGLYPH); return 0; } -int sceFontGetCharGlyphImage_Clip(u32 libHandler, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) -{ - ERROR_LOG(HLE, "sceFontGetCharGlyphImage_Clip %x, %x, %x (%c)", libHandler, charCode, glyphImagePtr, charCode); - //sceFontGetCharGlyphImage(libHandler, charCode, glyphImagePtr); +int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode) { + INFO_LOG(HLE, "sceFontSetAltCharacterCode(%08x) (%c)", fontLibHandle, charCode); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + fl->SetAltCharCode(charCode); + } return 0; } -int sceFontSetAltCharacterCode(u32 libHandle, u32 charCode) -{ - ERROR_LOG(HLE, "sceFontSetAltCharacterCode %x (%c)", libHandle, charCode); +int sceFontFlush(u32 fontHandle) { + INFO_LOG(HLE, "sceFontFlush(%i)", fontHandle); + // Probably don't need to do anything here. return 0; } -int sceFontFlush(u32 fontHandle) -{ - DEBUG_LOG(HLE, "sceFontFlush(%i)", fontHandle); +// One would think that this should loop through the fonts loaded in the fontLibHandle, +// but it seems not. +int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, u32 numFonts) { + ERROR_LOG(HLE, "sceFontGetFontList(%08x, %08x, %i)", fontLibHandle, fontStylePtr, numFonts); + numFonts = std::min(numFonts, (u32)internalFonts.size()); + for (u32 i = 0; i < numFonts; i++) + { + PGFFontStyle style = internalFonts[i]->GetFontStyle(); + Memory::WriteStruct(fontStylePtr, &style); + fontStylePtr += sizeof(style); + } return 0; } -int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, u32 numFonts) -{ - ERROR_LOG(HLE, "sceFontGetFontList %x, %x, %x", fontLibHandle, fontStylePtr, numFonts); +int sceFontGetNumFontList(u32 fontLibHandle, u32 errorCodePtr) { + INFO_LOG(HLE, "sceFontGetNumFontList(%08x, %08x)", fontLibHandle, errorCodePtr); + if (Memory::IsValidAddress(errorCodePtr)) + Memory::Write_U32(0, errorCodePtr); + return internalFonts.size(); +} - FontStyle style; - memset(&style, 0, sizeof (style)); +int sceFontSetResolution(u32 fontLibHandle, float hRes, float vRes) { + INFO_LOG(HLE, "sceFontSetResolution(%08x, %f, %f)", fontLibHandle, hRes, vRes); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + fl->SetResolution(hRes, vRes); + } + return 0; +} - style.fontH = 20 / 64.f; - style.fontV = 20 / 64.f; - style.fontHRes = 20 / 64.f; - style.fontVRes = 20 / 64.f; - style.fontStyle = 1; - //style.fontFamily +float sceFontPixelToPointH(int fontLibHandle, float fontPixelsH, u32 errorCodePtr) { + INFO_LOG(HLE, "sceFontPixelToPointH(%08x, %f, %08x)", fontLibHandle, fontPixelsH, errorCodePtr); + if (Memory::IsValidAddress(errorCodePtr)) + Memory::Write_U32(0, errorCodePtr); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + return fontPixelsH * pointDPI / fl->FontHRes(); + } + return 0; +} - for (u32 i = 0; i < numFonts; i++) - { - Memory::WriteStruct(fontStylePtr + (sizeof(style)) * i, &style); +float sceFontPixelToPointV(int fontLibHandle, float fontPixelsV, u32 errorCodePtr) { + INFO_LOG(HLE, "UNIMPL sceFontPixelToPointV(%08x, %f, %08x)", fontLibHandle, fontPixelsV, errorCodePtr); + if (Memory::IsValidAddress(errorCodePtr)) + Memory::Write_U32(0, errorCodePtr); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + return fontPixelsV * pointDPI / fl->FontVRes(); } return 0; } -int sceFontGetNumFontList(u32 libHandle, u32 errorCodePtr) -{ - ERROR_LOG(HLE, "UNIMPL sceFontGetNumFontList %x, %x", libHandle, errorCodePtr); +float sceFontPointToPixelH(int fontLibHandle, float fontPointsH, u32 errorCodePtr) { + INFO_LOG(HLE, "UNIMPL sceFontPointToPixelH(%08x, %f, %08x)", fontLibHandle, fontPointsH, errorCodePtr); if (Memory::IsValidAddress(errorCodePtr)) - { Memory::Write_U32(0, errorCodePtr); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + return fontPointsH * fl->FontHRes() / pointDPI; } - return 1; + return 0; } -int sceFontSetResolution(u32 fontLibHandle, float hRes, float vRes) -{ - ERROR_LOG(HLE, "UNIMPL sceFontSetResolution(%i, %f, %f)", fontLibHandle, hRes, vRes); +float sceFontPointToPixelV(int fontLibHandle, float fontPointsV, u32 errorCodePtr) { + INFO_LOG(HLE, "UNIMPL sceFontPointToPixelV(%08x, %f, %08x)", fontLibHandle, fontPointsV, errorCodePtr); + if (Memory::IsValidAddress(errorCodePtr)) + Memory::Write_U32(0, errorCodePtr); + FontLib *fl = GetFontLib(fontLibHandle); + if (fl) { + return fontPointsV * fl->FontVRes() / pointDPI; + } return 0; } @@ -402,10 +920,17 @@ int sceFontCalcMemorySize() { return 0; } +int sceFontGetShadowGlyphImage() { + ERROR_LOG(HLE, "UNIMPL sceFontGetShadowGlyphImage()"); + return 0; +} +int sceFontGetShadowGlyphImage_Clip() { + ERROR_LOG(HLE, "UNIMPL sceFontGetShadowGlyphImage_Clip()"); + return 0; +} -const HLEFunction sceLibFont[] = -{ +const HLEFunction sceLibFont[] = { {0x67f17ed7, WrapU_UU, "sceFontNewLib"}, {0x574b6fbc, WrapI_U, "sceFontDoneLib"}, {0x48293280, WrapI_UFF, "sceFontSetResolution"}, @@ -416,28 +941,27 @@ const HLEFunction sceLibFont[] = {0x2f67356a, WrapI_V, "sceFontCalcMemorySize"}, {0x5333322d, WrapI_UUUU, "sceFontGetFontInfoByIndexNumber"}, {0xa834319d, WrapU_UUUU, "sceFontOpen"}, - {0x57fcb733, WrapU_UUUU, "sceFontOpenUserFile"}, + {0x57fcb733, WrapU_UCUU, "sceFontOpenUserFile"}, {0xbb8e7fe6, WrapU_UUUU, "sceFontOpenUserMemory"}, {0x3aea8cb6, WrapI_U, "sceFontClose"}, {0x0da7535e, WrapI_UU, "sceFontGetFontInfo"}, {0xdcc80c2f, WrapI_UUU, "sceFontGetCharInfo"}, + {0xaa3de7b5, WrapI_UUU, "sceFontGetShadowInfo"}, {0x5c3e4a9e, WrapI_UUU, "sceFontGetCharImageRect"}, + {0x48b06520, WrapI_UUU, "sceFontGetShadowImageRect"}, {0x980f4895, WrapI_UUU, "sceFontGetCharGlyphImage"}, {0xca1e6945, WrapI_UUUIIII, "sceFontGetCharGlyphImage_Clip"}, - {0x74b21701, 0, "sceFontPixelToPointH"}, - {0xf8f0752e, 0, "sceFontPixelToPointV"}, - {0x472694cd, 0, "sceFontPointToPixelH"}, - {0x3c4b7e82, 0, "sceFontPointToPixelV"}, + {0x74b21701, WrapF_IFU, "sceFontPixelToPointH"}, + {0xf8f0752e, WrapF_IFU, "sceFontPixelToPointV"}, + {0x472694cd, WrapF_IFU, "sceFontPointToPixelH"}, + {0x3c4b7e82, WrapF_IFU, "sceFontPointToPixelV"}, {0xee232411, WrapI_UU, "sceFontSetAltCharacterCode"}, - {0xaa3de7b5, 0, "sceFontGetShadowInfo"}, - {0x48b06520, 0, "sceFontGetShadowImageRect"}, - {0x568be516, 0, "sceFontGetShadowGlyphImage"}, - {0x5dcf6858, 0, "sceFontGetShadowGlyphImage_Clip"}, + {0x568be516, WrapI_V, "sceFontGetShadowGlyphImage"}, + {0x5dcf6858, WrapI_V, "sceFontGetShadowGlyphImage_Clip"}, {0x02d7f94b, WrapI_U, "sceFontFlush"}, - }; -void Register_sceFont() -{ + +void Register_sceFont() { RegisterModule("sceLibFont", ARRAY_SIZE(sceLibFont), sceLibFont); } diff --git a/Core/HLE/sceFont.h b/Core/HLE/sceFont.h index 81d095c5bb17..c8b8cf9b079c 100644 --- a/Core/HLE/sceFont.h +++ b/Core/HLE/sceFont.h @@ -5,4 +5,5 @@ class PointerWrap; void Register_sceFont(); void __FontInit(); +void __FontShutdown(); void __FontDoState(PointerWrap &p); diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index 16e790d342e9..43bdbccf5af0 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -220,7 +220,7 @@ u32 sceGeDrawSync(u32 mode) int sceGeContinue() { - ERROR_LOG(HLE, "UNIMPL sceGeContinue"); + DEBUG_LOG(HLE, "UNIMPL sceGeContinue"); // no arguments return 0; } @@ -228,7 +228,7 @@ int sceGeContinue() int sceGeBreak(u32 mode) { //mode => 0 : current dlist 1: all drawing - ERROR_LOG(HLE, "UNIMPL sceGeBreak(mode=%d)", mode); + DEBUG_LOG(HLE, "UNIMPL sceGeBreak(mode=%d)", mode); return 0; } @@ -237,10 +237,10 @@ u32 sceGeSetCallback(u32 structAddr) DEBUG_LOG(HLE, "sceGeSetCallback(struct=%08x)", structAddr); int cbID = -1; - for (int i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) + for (size_t i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i) if (!ge_used_callbacks[i]) { - cbID = i; + cbID = (int) i; break; } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 7ea643ab06f1..9ea49f0056fb 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -31,6 +31,10 @@ #include "../FileSystems/ISOFileSystem.h" #include "../FileSystems/DirectoryFileSystem.h" +extern "C" { +#include "ext/libkirk/amctrl.h" +}; + #include "sceIo.h" #include "sceRtc.h" #include "sceKernel.h" @@ -76,6 +80,7 @@ umd01: block access - umd #define O_CREAT 0x0200 #define O_TRUNC 0x0400 #define O_NOWAIT 0x8000 +#define O_NPDRM 0x40000000 typedef s32 SceMode; @@ -128,7 +133,7 @@ struct dirent { class FileNode : public KernelObject { public: - FileNode() : callbackID(0), callbackArg(0), asyncResult(0), closePending(false), pendingAsyncResult(false), sectorBlockMode(false) {} + FileNode() : callbackID(0), callbackArg(0), asyncResult(0), pendingAsyncResult(false), sectorBlockMode(false), closePending(false), npdrm(0), pgdInfo(NULL) {} ~FileNode() { pspFileSystem.CloseFile(handle); } @@ -151,6 +156,9 @@ class FileNode : public KernelObject { p.Do(closePending); p.Do(info); p.Do(openMode); + + // TODO: Savestate PGD files? + p.DoMarker("File"); } @@ -168,8 +176,17 @@ class FileNode : public KernelObject { PSPFileInfo info; u32 openMode; + + u32 npdrm; + PGD_DESC *pgdInfo; }; +/******************************************************************************/ + + + +/******************************************************************************/ + static void TellFsThreadEnded (SceUID threadID) { pspFileSystem.ThreadEnded(threadID); } @@ -183,7 +200,7 @@ void __IoInit() { char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT]; char memstickpath[_MAX_PATH]; - char flashpath[_MAX_PATH]; + char flash0path[_MAX_PATH]; GetModuleFileName(NULL,path_buffer,sizeof(path_buffer)); @@ -195,25 +212,26 @@ void __IoInit() { _splitpath_s(path_buffer, drive, dir, file, ext ); // Mount a couple of filesystems - sprintf(memstickpath, "%s%sMemStick\\", drive, dir); - sprintf(flashpath, "%s%sFlash\\", drive, dir); + sprintf(memstickpath, "%s%smemstick\\", drive, dir); + sprintf(flash0path, "%s%sflash0\\", drive, dir); #else // TODO std::string memstickpath = g_Config.memCardDirectory; - std::string flashpath = g_Config.flashDirectory; + std::string flash0path = g_Config.flashDirectory; #endif - DirectoryFileSystem *memstick; - DirectoryFileSystem *flash; - memstick = new DirectoryFileSystem(&pspFileSystem, memstickpath); - flash = new DirectoryFileSystem(&pspFileSystem, flashpath); + DirectoryFileSystem *memstick = new DirectoryFileSystem(&pspFileSystem, memstickpath); +#ifdef ANDROID + VFSFileSystem *flash0 = new VFSFileSystem(&pspFileSystem, "flash0"); +#else + DirectoryFileSystem *flash0 = new DirectoryFileSystem(&pspFileSystem, flash0path); +#endif pspFileSystem.Mount("ms0:", memstick); pspFileSystem.Mount("fatms0:", memstick); pspFileSystem.Mount("fatms:", memstick); - pspFileSystem.Mount("flash0:", flash); - pspFileSystem.Mount("flash1:", flash); + pspFileSystem.Mount("flash0:", flash0); __KernelListenThreadEnd(&TellFsThreadEnded); } @@ -255,7 +273,7 @@ u32 sceIoAssign(u32 alias_addr, u32 physical_addr, u32 filesystem_addr, int mode perm = "IOASSIGN_RDONLY"; break; default: - perm = "unhandled " + mode; + perm = "unhandled"; break; } DEBUG_LOG(HLE, "sceIoAssign(%s, %s, %s, %s, %08x, %i)", alias.c_str(), physical_dev.c_str(), filesystem_dev.c_str(), perm.c_str(), arg_addr, argSize); @@ -342,6 +360,42 @@ u32 sceIoGetstat(const char *filename, u32 addr) { } } +u32 npdrmRead(FileNode *f, u8 *data, int size) { + PGD_DESC *pgd = f->pgdInfo; + u32 block, offset; + u32 remain_size, copy_size; + + block = pgd->file_offset/pgd->block_size; + offset = pgd->file_offset%pgd->block_size; + + remain_size = size; + + while(remain_size){ + + if(pgd->current_block!=block){ + pspFileSystem.ReadFile(f->handle, pgd->block_buf, pgd->block_size); + pgd_decrypt_block(pgd, block); + pgd->current_block = block; + } + + if(offset+remain_size>pgd->block_size){ + copy_size = pgd->block_size-offset; + block += 1; + offset = 0; + }else{ + copy_size = remain_size; + } + + memcpy(data, pgd->block_buf+offset, copy_size); + + data += copy_size; + remain_size -= copy_size; + pgd->file_offset += copy_size; + } + + return size; +} + //Not sure about wrapping it or not, since the log seems to take the address of the data var u32 sceIoRead(int id, u32 data_addr, int size) { if (id == 3) { @@ -358,7 +412,11 @@ u32 sceIoRead(int id, u32 data_addr, int size) { } else if (Memory::IsValidAddress(data_addr)) { u8 *data = (u8*) Memory::GetPointer(data_addr); - f->asyncResult = (u32) pspFileSystem.ReadFile(f->handle, data, size); + if(f->npdrm){ + f->asyncResult = (u32) npdrmRead(f, data, size); + }else{ + f->asyncResult = (u32) pspFileSystem.ReadFile(f->handle, data, size); + } DEBUG_LOG(HLE, "%i=sceIoRead(%d, %08x , %i)", f->asyncResult, id, data_addr, size); return f->asyncResult; } else { @@ -445,6 +503,26 @@ u32 sceIoCancel(int id) return error; } +u32 npdrmLseek(FileNode *f, s32 where, FileMove whence) +{ + u32 newPos; + + if(whence==FILEMOVE_BEGIN){ + newPos = where; + }else if(whence==FILEMOVE_CURRENT){ + newPos = f->pgdInfo->file_offset+where; + }else{ + newPos = f->pgdInfo->data_size+where; + } + + if(newPos<0 || newPos>f->pgdInfo->data_size){ + return -EINVAL; + } + + f->pgdInfo->file_offset = newPos; + return newPos; +} + s64 sceIoLseek(int id, s64 offset, int whence) { u32 error; FileNode *f = kernelObjects.Get < FileNode > (id, error); @@ -468,7 +546,11 @@ s64 sceIoLseek(int id, s64 offset, int whence) { if(newPos < 0) return -1; - f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + if(f->npdrm){ + f->asyncResult = npdrmLseek(f, (s32)offset, seek); + }else{ + f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + } DEBUG_LOG(HLE, "%i = sceIoLseek(%d,%i,%i)", f->asyncResult, id, (int) offset, whence); return f->asyncResult; } else { @@ -500,7 +582,11 @@ u32 sceIoLseek32(int id, int offset, int whence) { if(newPos < 0) return -1; - f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + if(f->npdrm){ + f->asyncResult = npdrmLseek(f, (s32)offset, seek); + }else{ + f->asyncResult = (u32) pspFileSystem.SeekFile(f->handle, (s32) offset, seek); + } DEBUG_LOG(HLE, "%i = sceIoLseek32(%d,%i,%i)", f->asyncResult, id, (int) offset, whence); return f->asyncResult; } else { @@ -537,12 +623,20 @@ u32 sceIoOpen(const char* filename, int flags, int mode) { f->asyncResult = id; f->info = info; f->openMode = access; + + f->npdrm = (flags & O_NPDRM)? true: false; + DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode); return id; } u32 sceIoClose(int id) { + u32 error; DEBUG_LOG(HLE, "sceIoClose(%d)", id); + FileNode *f = kernelObjects.Get < FileNode > (id, error); + if(f && f->npdrm){ + pgd_close(f->pgdInfo); + } return kernelObjects.Destroy < FileNode > (id); } @@ -1091,8 +1185,17 @@ u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 ou case 0x04100001: if (Memory::IsValidAddress(indataPtr) && inlen == 16) { u8 keybuf[16]; + u8 pgd_header[0x90]; + INFO_LOG(HLE, "Decrypting PGD DRM files"); memcpy(keybuf, Memory::GetPointer(indataPtr), 16); - ERROR_LOG(HLE, "PGD DRM not yet supported, sorry."); + pspFileSystem.ReadFile(f->handle, pgd_header, 0x90); + f->pgdInfo = pgd_open(pgd_header, 2, keybuf); + if(f->pgdInfo==NULL){ + DEBUG_LOG(HLE, "Not a valid PGD file. Open as normal file."); + f->npdrm = false; + pspFileSystem.SeekFile(f->handle, (s32)0, FILEMOVE_BEGIN); + } + f->asyncResult = 0; } break; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 04e5a8368e02..06fe0bf0e837 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -60,6 +60,7 @@ #include "scePsmf.h" #include "sceImpose.h" #include "sceUsb.h" +#include "scePspNpDrm_user.h" #include "../Util/PPGeDraw.h" @@ -87,6 +88,7 @@ void __KernelInit() __KernelMemoryInit(); __KernelThreadingInit(); __KernelAlarmInit(); + __KernelVTimerInit(); __KernelEventFlagInit(); __KernelMbxInit(); __KernelMutexInit(); @@ -107,6 +109,7 @@ void __KernelInit() __ImposeInit(); __UsbInit(); __FontInit(); + SaveState::Init(); // Must be after IO, as it may create a directory // "Internal" PSP libraries @@ -127,6 +130,8 @@ void __KernelShutdown() INFO_LOG(HLE, "Shutting down kernel - %i kernel objects alive", kernelObjects.GetCount()); kernelObjects.Clear(); + __FontShutdown(); + __MpegShutdown(); __PsmfShutdown(); __PPGeShutdown(); @@ -161,6 +166,7 @@ void __KernelDoState(PointerWrap &p) __KernelMemoryDoState(p); __KernelThreadingDoState(p); __KernelAlarmDoState(p); + __KernelVTimerDoState(p); __KernelEventFlagDoState(p); __KernelMbxDoState(p); __KernelModuleDoState(p); @@ -519,55 +525,94 @@ struct SystemStatus { SceUInt perfcounter3; }; -u32 sceKernelReferSystemStatus(u32 statusPtr) -{ +int sceKernelReferSystemStatus(u32 statusPtr) { DEBUG_LOG(HLE, "sceKernelReferSystemStatus(%08x)", statusPtr); if (Memory::IsValidAddress(statusPtr)) { SystemStatus status; memset(&status, 0, sizeof(SystemStatus)); status.size = sizeof(SystemStatus); + // TODO: Fill in the struct! Memory::WriteStruct(statusPtr, &status); } return 0; } -u32 sceKernelReferGlobalProfiler(u32 statusPtr) { - DEBUG_LOG(HLE, "sceKernelReferGlobalProfiler(%08x)", statusPtr); +struct DebugProfilerRegs { + u32 enable; + u32 systemck; + u32 cpuck; + u32 internal; + u32 memory; + u32 copz; + u32 vfpu; + u32 sleep; + u32 bus_access; + u32 uncached_load; + u32 uncached_store; + u32 cached_load; + u32 cached_store; + u32 i_miss; + u32 d_miss; + u32 d_writeback; + u32 cop0_inst; + u32 fpu_inst; + u32 vfpu_inst; + u32 local_bus; +}; + +u32 sceKernelReferThreadProfiler(u32 statusPtr) { + ERROR_LOG(HLE, "FAKE sceKernelReferThreadProfiler()"); + + // Can we confirm that the struct above is the right struct? + // If so, re-enable this code. + //DebugProfilerRegs regs; + //memset(®s, 0, sizeof(regs)); + // TODO: fill the struct. + //if (Memory::IsValidAddress(statusPtr)) { + // Memory::WriteStruct(statusPtr, ®s); + //} + return 0; +} + +int sceKernelReferGlobalProfiler(u32 statusPtr) { + DEBUG_LOG(HLE, "UNIMPL sceKernelReferGlobalProfiler(%08x)", statusPtr); // Ignore for now return 0; } const HLEFunction ThreadManForUser[] = { - {0x55C20A00,&WrapI_CUUU, "sceKernelCreateEventFlag"}, - {0x812346E4,&WrapU_IU, "sceKernelClearEventFlag"}, - {0xEF9E4C70,&WrapU_I, "sceKernelDeleteEventFlag"}, - {0x1fb15a32,&WrapU_IU, "sceKernelSetEventFlag"}, - {0x402FCF22,&WrapI_IUUUU, "sceKernelWaitEventFlag"}, - {0x328C546A,&WrapI_IUUUU, "sceKernelWaitEventFlagCB"}, - {0x30FD48F0,&WrapI_IUUUU, "sceKernelPollEventFlag"}, - {0xCD203292,&WrapU_IUU, "sceKernelCancelEventFlag"}, - {0xA66B0120,&WrapU_IU, "sceKernelReferEventFlagStatus"}, - - {0x8FFDF9A2,&WrapI_IIU, "sceKernelCancelSema"}, - {0xD6DA4BA1,&WrapI_CUIIU, "sceKernelCreateSema"}, - {0x28b6489c,&WrapI_I, "sceKernelDeleteSema"}, - {0x58b1f937,&WrapI_II, "sceKernelPollSema"}, - {0xBC6FEBC5,&WrapI_IU, "sceKernelReferSemaStatus"}, - {0x3F53E640,&WrapI_II, "sceKernelSignalSema"}, - {0x4E3A1105,&WrapI_IIU, "sceKernelWaitSema"}, - {0x6d212bac,&WrapI_IIU, "sceKernelWaitSemaCB"}, - - {0x60107536,&WrapI_U, "sceKernelDeleteLwMutex"}, - {0x19CFF145,&WrapI_UCUIU, "sceKernelCreateLwMutex"}, - {0xf8170fbe,&WrapI_I, "sceKernelDeleteMutex"}, - {0xB011B11F,&WrapI_IIU, "sceKernelLockMutex"}, - {0x5bf4dd27,&WrapI_IIU, "sceKernelLockMutexCB"}, - {0x6b30100f,&WrapI_II, "sceKernelUnlockMutex"}, - {0xb7d098c6,&WrapI_CUIU, "sceKernelCreateMutex"}, - {0x0DDCD2C9,&WrapI_II, "sceKernelTryLockMutex"}, - {0xA9C2CB9A,&WrapI_IU, "sceKernelReferMutexStatus"}, - // NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp. + {0x55C20A00,&WrapI_CUUU, "sceKernelCreateEventFlag"}, + {0x812346E4,&WrapU_IU, "sceKernelClearEventFlag"}, + {0xEF9E4C70,&WrapU_I, "sceKernelDeleteEventFlag"}, + {0x1fb15a32,&WrapU_IU, "sceKernelSetEventFlag"}, + {0x402FCF22,&WrapI_IUUUU, "sceKernelWaitEventFlag"}, + {0x328C546A,&WrapI_IUUUU, "sceKernelWaitEventFlagCB"}, + {0x30FD48F0,&WrapI_IUUUU, "sceKernelPollEventFlag"}, + {0xCD203292,&WrapU_IUU, "sceKernelCancelEventFlag"}, + {0xA66B0120,&WrapU_IU, "sceKernelReferEventFlagStatus"}, + + {0x8FFDF9A2,&WrapI_IIU, "sceKernelCancelSema"}, + {0xD6DA4BA1,&WrapI_CUIIU, "sceKernelCreateSema"}, + {0x28b6489c,&WrapI_I, "sceKernelDeleteSema"}, + {0x58b1f937,&WrapI_II, "sceKernelPollSema"}, + {0xBC6FEBC5,&WrapI_IU, "sceKernelReferSemaStatus"}, + {0x3F53E640,&WrapI_II, "sceKernelSignalSema"}, + {0x4E3A1105,&WrapI_IIU, "sceKernelWaitSema"}, + {0x6d212bac,&WrapI_IIU, "sceKernelWaitSemaCB"}, + + {0x60107536,&WrapI_U, "sceKernelDeleteLwMutex"}, + {0x19CFF145,&WrapI_UCUIU, "sceKernelCreateLwMutex"}, + {0x4C145944,&WrapI_IU, "sceKernelReferLwMutexStatusByID"}, + // NOTE: LockLwMutex, UnlockLwMutex, and ReferLwMutexStatus are in Kernel_Library, see sceKernelInterrupt.cpp. + + {0xf8170fbe,&WrapI_I, "sceKernelDeleteMutex"}, + {0xB011B11F,&WrapI_IIU, "sceKernelLockMutex"}, + {0x5bf4dd27,&WrapI_IIU, "sceKernelLockMutexCB"}, + {0x6b30100f,&WrapI_II, "sceKernelUnlockMutex"}, + {0xb7d098c6,&WrapI_CUIU, "sceKernelCreateMutex"}, + {0x0DDCD2C9,&WrapI_II, "sceKernelTryLockMutex"}, + {0xA9C2CB9A,&WrapI_IU, "sceKernelReferMutexStatus"}, {0xFCCFAD26,sceKernelCancelWakeupThread,"sceKernelCancelWakeupThread"}, {0xea748e31,sceKernelChangeCurrentThreadAttr,"sceKernelChangeCurrentThreadAttr"}, @@ -595,7 +640,7 @@ const HLEFunction ThreadManForUser[] = {0x82826f70,sceKernelSleepThreadCB,"sceKernelSleepThreadCB"}, {0xF475845D,&WrapI_IUU,"sceKernelStartThread"}, {0x9944f31f,sceKernelSuspendThread,"sceKernelSuspendThread"}, - {0x616403ba,WrapI_U,"sceKernelTerminateThread"}, + {0x616403ba,WrapI_I,"sceKernelTerminateThread"}, {0x383f7bcc,WrapI_I,"sceKernelTerminateDeleteThread"}, {0x840E8133,WrapI_IU,"sceKernelWaitThreadEndCB"}, {0xd13bde95,sceKernelCheckThreadStack,"sceKernelCheckThreadStack"}, @@ -608,9 +653,9 @@ const HLEFunction ThreadManForUser[] = {0xdb738f35,WrapI_U,"sceKernelGetSystemTime"}, {0x369ed59d,WrapU_V,"sceKernelGetSystemTimeLow"}, - {0x8218B4DD,&WrapU_U,"sceKernelReferGlobalProfiler"}, - {0x627E6F3A,&WrapU_U,"sceKernelReferSystemStatus"}, - {0x64D4540E,0,"sceKernelReferThreadProfiler"}, + {0x8218B4DD,WrapI_U,"sceKernelReferGlobalProfiler"}, + {0x627E6F3A,WrapI_U,"sceKernelReferSystemStatus"}, + {0x64D4540E,WrapU_U,"sceKernelReferThreadProfiler"}, //Fifa Street 2 uses alarms {0x6652b8ca,WrapI_UUU,"sceKernelSetAlarm"}, diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 17911153d98e..65df209f356c 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -87,6 +87,7 @@ enum SCE_KERNEL_ERROR_ILLEGAL_CHUNK_ID = 0x800200de, SCE_KERNEL_ERROR_NOCHUNK = 0x800200df, SCE_KERNEL_ERROR_NO_FREECHUNK = 0x800200e0, + SCE_ERROR_KERNEL_ILLEGAL_ALIGNMENT_SIZE = 0x800200e4, SCE_KERNEL_ERROR_LINKERR = 0x8002012c, SCE_KERNEL_ERROR_ILLEGAL_OBJECT = 0x8002012d, SCE_KERNEL_ERROR_UNKNOWN_MODULE = 0x8002012e, diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 5241b1d9374c..b0196b75d901 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -213,7 +213,7 @@ void __InterruptsInit() { interruptsEnabled = 1; inInterrupt = false; - for(int i = 0; i < ARRAY_SIZE(intrHandlers); ++i) + for (int i = 0; i < (int)ARRAY_SIZE(intrHandlers); ++i) intrHandlers[i] = new IntrHandler(i); intState.clear(); } @@ -246,11 +246,11 @@ void __InterruptsDoStateLate(PointerWrap &p) void __InterruptsShutdown() { - for (int i = 0; i < ARRAY_SIZE(intrHandlers); ++i) + for (size_t i = 0; i < ARRAY_SIZE(intrHandlers); ++i) intrHandlers[i]->clear(); - for(int i = 0; i < ARRAY_SIZE(intrHandlers); ++i) + for (size_t i = 0; i < ARRAY_SIZE(intrHandlers); ++i) { - if(intrHandlers[i]) + if (intrHandlers[i]) { delete intrHandlers[i]; intrHandlers[i] = 0; @@ -411,7 +411,7 @@ SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 return subIntrHandler; } -u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber) +int __ReleaseSubIntrHandler(int intrNumber, int subIntrNumber) { if (!intrHandlers[intrNumber]->has(subIntrNumber)) return -1; @@ -446,9 +446,9 @@ u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handl return error; } -u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber) +int sceKernelReleaseSubIntrHandler(int intrNumber, int subIntrNumber) { - DEBUG_LOG(HLE,"sceKernelReleaseSubIntrHandler(%i, %i)", PARAM(0), PARAM(1)); + DEBUG_LOG(HLE, "sceKernelReleaseSubIntrHandler(%i, %i)", intrNumber, subIntrNumber); if (intrNumber >= PSP_NUMBER_INTERRUPTS) return -1; @@ -553,6 +553,7 @@ const HLEFunction Kernel_Library[] = {0xbea46419,WrapI_UIU, "sceKernelLockLwMutex"}, {0x1FC64E09,WrapI_UIU, "sceKernelLockLwMutexCB"}, {0x15b6446b,WrapI_UI, "sceKernelUnlockLwMutex"}, + {0xc1734599,WrapI_UU, "sceKernelReferLwMutexStatus"}, {0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"}, {0x1839852A,WrapU_UUU,"sce_paf_private_memcpy"}, }; @@ -566,7 +567,7 @@ void Register_Kernel_Library() const HLEFunction InterruptManager[] = { {0xCA04A2B9, WrapU_UUUU, "sceKernelRegisterSubIntrHandler"}, - {0xD61E6961, WrapU_UU, "sceKernelReleaseSubIntrHandler"}, + {0xD61E6961, WrapI_II, "sceKernelReleaseSubIntrHandler"}, {0xFB8E22EC, WrapU_UU, "sceKernelEnableSubIntr"}, {0x8A389411, WrapU_UU, "sceKernelDisableSubIntr"}, {0x5CB5A78B, 0, "sceKernelSuspendSubIntr"}, diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index b1e27ab125ad..7c0ef381ce57 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -139,10 +139,10 @@ void __KernelReturnFromInterrupt(); void __RegisterIntrHandler(u32 intrNumber, IntrHandler* handler); SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error); -u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber); +int __ReleaseSubIntrHandler(int intrNumber, int subIntrNumber); u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg); -u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber); +int sceKernelReleaseSubIntrHandler(int intrNumber, int subIntrNumber); u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber); void Register_Kernel_Library(); diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index b4d8f9d9d95a..16b5ac3bcb78 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -390,6 +390,14 @@ void sceKernelReferFplStatus() ////////////////////////////////////////////////////////////////////////// //00:49:12 ector, well the partitions are 1 = kernel, 2 = user, 3 = me, 4 = kernel mirror :) +enum MemblockType +{ + PSP_SMEM_Low = 0, + PSP_SMEM_High = 1, + PSP_SMEM_Addr = 2, + PSP_SMEM_LowAligned = 3, + PSP_SMEM_HighAligned = 4, +}; class PartitionMemoryBlock : public KernelObject { @@ -401,23 +409,34 @@ class PartitionMemoryBlock : public KernelObject int sz = alloc->GetBlockSizeFromAddress(address); sprintf(ptr, "MemPart: %08x - %08x size: %08x", address, address + sz, sz); } - static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MPPID; } /// ???? + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_UID; } int GetIDType() const { return PPSSPP_KERNEL_TMID_PMB; } - PartitionMemoryBlock(BlockAllocator *_alloc, u32 size, bool fromEnd) + PartitionMemoryBlock(BlockAllocator *_alloc, const char *_name, u32 size, MemblockType type, u32 alignment) { alloc = _alloc; + strncpy(name, _name, 32); + name[31] = '\0'; // 0 is used for save states to wake up. if (size != 0) { - address = alloc->Alloc(size, fromEnd, "PMB"); + if (type == PSP_SMEM_Addr) + { + alignment &= ~0xFF; + address = alloc->AllocAt(alignment, size, name); + } + else if (type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) + address = alloc->AllocAligned(size, 0x100, alignment, type == PSP_SMEM_HighAligned, name); + else + address = alloc->Alloc(size, type == PSP_SMEM_High, name); alloc->ListBlocks(); } } ~PartitionMemoryBlock() { - alloc->Free(address); + if (address != (u32)-1) + alloc->Free(address); } bool IsValid() {return address != (u32)-1;} BlockAllocator *alloc; @@ -449,54 +468,78 @@ void sceKernelTotalFreeMemSize() RETURN(retVal); } -void sceKernelAllocPartitionMemory() +int sceKernelAllocPartitionMemory(int partition, const char *name, int type, u32 size, u32 addr) { - int partid = PARAM(0); - const char *name = Memory::GetCharPointer(PARAM(1)); - int type = PARAM(2); - u32 size = PARAM(3); - int addr = PARAM(4); //only if type includes ADDR + if (name == NULL) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid name", SCE_KERNEL_ERROR_ERROR); + return SCE_KERNEL_ERROR_ERROR; + } + if (size == 0) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid size %x", SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED, size); + return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED; + } + if (partition < 1 || partition > 9 || partition == 7) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition); + return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT; + } + // We only support user right now. + if (partition != 2 && partition != 5 && partition != 6) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_PARTITION, partition); + return SCE_KERNEL_ERROR_ILLEGAL_PARTITION; + } + if (type < PSP_SMEM_Low || type > PSP_SMEM_HighAligned) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid type %x", SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE, type); + return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE; + } + // Alignment is only allowed for powers of 2. + if ((type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) && ((addr & (addr - 1)) != 0 || addr == 0)) + { + WARN_LOG(HLE, "%08x=sceKernelAllocPartitionMemory(): invalid alignment %x", SCE_ERROR_KERNEL_ILLEGAL_ALIGNMENT_SIZE, addr); + return SCE_ERROR_KERNEL_ILLEGAL_ALIGNMENT_SIZE; + } - PartitionMemoryBlock *block = new PartitionMemoryBlock(&userMemory, size, type==1); + PartitionMemoryBlock *block = new PartitionMemoryBlock(&userMemory, name, size, (MemblockType)type, addr); if (!block->IsValid()) { + delete block; ERROR_LOG(HLE, "ARGH! sceKernelAllocPartitionMemory failed"); - RETURN(-1); + return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED; } SceUID uid = kernelObjects.Create(block); - strncpy(block->name, name, 32); DEBUG_LOG(HLE,"%i = sceKernelAllocPartitionMemory(partition = %i, %s, type= %i, size= %i, addr= %08x)", - uid, partid,name,type,size,addr); + uid, partition, name, type, size, addr); if (type == 2) ERROR_LOG(HLE, "ARGH! sceKernelAllocPartitionMemory wants a specific address"); - RETURN(uid); //for now + return uid; } -void sceKernelFreePartitionMemory() +int sceKernelFreePartitionMemory(SceUID id) { - SceUID id = PARAM(0); DEBUG_LOG(HLE,"sceKernelFreePartitionMemory(%d)",id); - RETURN(kernelObjects.Destroy(id)); + return kernelObjects.Destroy(id); } -void sceKernelGetBlockHeadAddr() +u32 sceKernelGetBlockHeadAddr(SceUID id) { - SceUID id = PARAM(0); - u32 error; PartitionMemoryBlock *block = kernelObjects.Get(id, error); if (block) { DEBUG_LOG(HLE,"%08x = sceKernelGetBlockHeadAddr(%i)", block->address, id); - RETURN(block->address); + return block->address; } else { ERROR_LOG(HLE,"sceKernelGetBlockHeadAddr failed(%i)", id); - RETURN(error); + return 0; } } @@ -719,7 +762,7 @@ KernelObject *__KernelMemoryVPLObject() KernelObject *__KernelMemoryPMBObject() { // TODO: We could theoretically handle kernelMemory too, but we don't support that now anyway. - return new PartitionMemoryBlock(&userMemory, 0, true); + return new PartitionMemoryBlock(&userMemory, "", 0, PSP_SMEM_Low, 0); } // VPL = variable length memory pool @@ -843,7 +886,7 @@ SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize // We ignore the upalign to 256 and do it ourselves by 8. u32 allocSize = vplSize; u32 memBlockPtr = userMemory.Alloc(allocSize, (attr & PSP_VPL_ATTR_HIGHMEM) != 0, "VPL"); - if (memBlockPtr == -1) + if (memBlockPtr == (u32)-1) { ERROR_LOG(HLE, "sceKernelCreateVpl: Failed to allocate %i bytes of pool data", vplSize); return SCE_KERNEL_ERROR_NO_MEMORY; @@ -1135,9 +1178,9 @@ const HLEFunction SysMemUserForUser[] = { {0xA291F107,sceKernelMaxFreeMemSize,"sceKernelMaxFreeMemSize"}, {0xF919F628,sceKernelTotalFreeMemSize,"sceKernelTotalFreeMemSize"}, {0x3FC9AE6A,WrapU_V,"sceKernelDevkitVersion"}, - {0x237DBD4F,sceKernelAllocPartitionMemory,"sceKernelAllocPartitionMemory"}, //(int size) ? - {0xB6D61D02,sceKernelFreePartitionMemory,"sceKernelFreePartitionMemory"}, //(void *ptr) ? - {0x9D9A5BA1,sceKernelGetBlockHeadAddr,"sceKernelGetBlockHeadAddr"}, //(void *ptr) ? + {0x237DBD4F,WrapI_ICIUU,"sceKernelAllocPartitionMemory"}, //(int size) ? + {0xB6D61D02,WrapI_I,"sceKernelFreePartitionMemory"}, //(void *ptr) ? + {0x9D9A5BA1,WrapU_I,"sceKernelGetBlockHeadAddr"}, //(void *ptr) ? {0x13a5abef,sceKernelPrintf,"sceKernelPrintf 0x13a5abef"}, {0x7591c7db,&WrapV_I,"sceKernelSetCompiledSdkVersion"}, {0x342061E5,&WrapV_I,"sceKernelSetCompiledSdkVersion370"}, diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 294b789a528d..97822c5a63e5 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -559,6 +559,10 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro } module->nm.entry_addr = reader.GetEntryPoint(); + + // use module_start_func instead of entry_addr if entry_addr is 0 + if (module->nm.entry_addr == 0) + module->nm.entry_addr = module->nm.module_start_func; if (newptr) { @@ -644,7 +648,7 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options) { - if (m->nm.module_start_func != 0 && m->nm.module_start_func != -1) + if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1) { if (m->nm.module_start_func != m->nm.entry_addr) WARN_LOG(LOADER, "Main module has start func (%08x) different from entry (%08x)?", m->nm.module_start_func, m->nm.entry_addr); @@ -837,9 +841,9 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu moduleId,argsize,argAddr,returnValueAddr,optionAddr); u32 entryAddr = module->nm.entry_addr; - if (entryAddr == -1 || entryAddr == module->memoryBlockAddr - 1) { + if (entryAddr == (u32)-1 || entryAddr == module->memoryBlockAddr - 1) { // TODO: Do these always take effect, or do they not override optionAddr? - if (module->nm.module_start_func != 0 && module->nm.module_start_func != -1) { + if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1) { entryAddr = module->nm.module_start_func; priority = module->nm.module_start_thread_priority; attr = module->nm.module_start_thread_attr; @@ -924,7 +928,7 @@ u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr) { u32 error; u32 handle = __IoGetFileHandleFromId(id, error); - if (handle == -1) { + if (handle == (u32)-1) { ERROR_LOG(HLE,"sceKernelLoadModuleByID(%08x, %08x, %08x): could not open file id",id,flags,lmoptionPtr); return error; } diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index a1828cb2e3f6..cd87a8f190be 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -46,7 +46,6 @@ #define PSP_LWMUTEX_ERROR_UNLOCK_UNDERFLOW 0x800201CE #define PSP_LWMUTEX_ERROR_ALREADY_LOCKED 0x800201CF -// Guesswork - not exposed anyway struct NativeMutex { SceSize size; @@ -54,6 +53,8 @@ struct NativeMutex SceUInt attr; int initialCount; int lockLevel; + SceUID lockThread; + // Not kept up to date. int numWaitThreads; }; @@ -69,22 +70,28 @@ struct Mutex : public KernelObject p.Do(nm); SceUID dv = 0; p.Do(waitingThreads, dv); - p.Do(lockThread); p.DoMarker("Mutex"); } NativeMutex nm; std::vector waitingThreads; - int lockThread; // The thread holding the lock }; -// Guesswork - not exposed anyway + struct NativeLwMutex { SceSize size; char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; SceUInt attr; - SceUInt workareaPtr; + SceUID uid; + u32 workareaPtr; + int initialCount; + // Not kept up to date. + int currentCount; + // Not kept up to date. + SceUID lockThread; + // Not kept up to date. + int numWaitThreads; }; struct NativeLwMutexWorkarea @@ -178,7 +185,7 @@ void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread) mutexHeldLocks.insert(std::make_pair(thread, mutex->GetUID())); mutex->nm.lockLevel = count; - mutex->lockThread = thread; + mutex->nm.lockThread = thread; } void __KernelMutexAcquireLock(Mutex *mutex, int count) @@ -188,10 +195,10 @@ void __KernelMutexAcquireLock(Mutex *mutex, int count) void __KernelMutexEraseLock(Mutex *mutex) { - if (mutex->lockThread != -1) + if (mutex->nm.lockThread != -1) { SceUID id = mutex->GetUID(); - std::pair locked = mutexHeldLocks.equal_range(mutex->lockThread); + std::pair locked = mutexHeldLocks.equal_range(mutex->nm.lockThread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { if ((*iter).second == id) @@ -201,7 +208,7 @@ void __KernelMutexEraseLock(Mutex *mutex) } } } - mutex->lockThread = -1; + mutex->nm.lockThread = -1; } std::vector::iterator __KernelMutexFindPriority(std::vector &waiting) @@ -245,7 +252,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio Mutex *mutex = new Mutex(); SceUID id = kernelObjects.Create(mutex); - mutex->nm.size = sizeof(mutex); + mutex->nm.size = sizeof(mutex->nm); strncpy(mutex->nm.name, name, KERNELOBJECT_MAX_NAME_LENGTH); mutex->nm.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; mutex->nm.attr = attr; @@ -253,7 +260,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio if (initialCount == 0) { mutex->nm.lockLevel = 0; - mutex->lockThread = -1; + mutex->nm.lockThread = -1; } else __KernelMutexAcquireLock(mutex, initialCount); @@ -307,7 +314,7 @@ int sceKernelDeleteMutex(SceUID id) for (iter = mutex->waitingThreads.begin(), end = mutex->waitingThreads.end(); iter != end; ++iter) wokeThreads |= __KernelUnlockMutexForThread(mutex, *iter, error, SCE_KERNEL_ERROR_WAIT_DELETE); - if (mutex->lockThread != -1) + if (mutex->nm.lockThread != -1) __KernelMutexEraseLock(mutex); mutex->waitingThreads.clear(); @@ -343,7 +350,7 @@ bool __KernelLockMutex(Mutex *mutex, int count, u32 &error) return true; } - if (mutex->lockThread == __KernelGetCurThread()) + if (mutex->nm.lockThread == __KernelGetCurThread()) { // Recursive mutex, let's just increase the lock count and keep going if (mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) @@ -379,7 +386,7 @@ bool __KernelUnlockMutex(Mutex *mutex, u32 &error) } if (!wokeThreads) - mutex->lockThread = -1; + mutex->nm.lockThread = -1; return wokeThreads; } @@ -528,7 +535,7 @@ int sceKernelUnlockMutex(SceUID id, int count) return SCE_KERNEL_ERROR_ILLEGAL_COUNT; if ((mutex->nm.attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && count > 1) return SCE_KERNEL_ERROR_ILLEGAL_COUNT; - if (mutex->nm.lockLevel == 0 || mutex->lockThread != __KernelGetCurThread()) + if (mutex->nm.lockLevel == 0 || mutex->nm.lockThread != __KernelGetCurThread()) return PSP_MUTEX_ERROR_NOT_LOCKED; if (mutex->nm.lockLevel < count) return PSP_MUTEX_ERROR_UNLOCK_UNDERFLOW; @@ -544,6 +551,32 @@ int sceKernelUnlockMutex(SceUID id, int count) return 0; } +int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) +{ + u32 error; + Mutex *m = kernelObjects.Get(id, error); + if (!m) + { + ERROR_LOG(HLE, "sceKernelReferMutexStatus(%i, %08x): invalid mutex id", id, infoAddr); + return error; + } + + DEBUG_LOG(HLE, "sceKernelReferMutexStatus(%08x, %08x)", id, infoAddr); + + // Should we crash the thread somehow? + if (!Memory::IsValidAddress(infoAddr)) + return -1; + + // Don't write if the size is 0. Anything else is A-OK, though, apparently. + if (Memory::Read_U32(infoAddr) != 0) + { + // Refresh and write + m->nm.numWaitThreads = m->waitingThreads.size(); + Memory::WriteStruct(infoAddr, &m->nm); + } + return 0; +} + int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int initialCount, u32 optionsPtr) { if (!name) @@ -564,11 +597,13 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init LwMutex *mutex = new LwMutex(); SceUID id = kernelObjects.Create(mutex); - mutex->nm.size = sizeof(mutex); + mutex->nm.size = sizeof(mutex->nm); strncpy(mutex->nm.name, name, KERNELOBJECT_MAX_NAME_LENGTH); mutex->nm.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; mutex->nm.attr = attr; + mutex->nm.uid = id; mutex->nm.workareaPtr = workareaPtr; + mutex->nm.initialCount = initialCount; NativeLwMutexWorkarea workarea; workarea.init(); workarea.lockLevel = initialCount; @@ -591,26 +626,6 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init return 0; } -int sceKernelReferMutexStatus(SceUID id, u32 infoAddr) -{ - u32 error; - Mutex *m = kernelObjects.Get(id, error); - if (!m) - { - ERROR_LOG(HLE, "sceKernelReferMutexStatus(%i, %08x): invalid mbx id", id, infoAddr); - return error; - } - - // Should we crash the thread somehow? - if (!Memory::IsValidAddress(infoAddr)) - return -1; - - // Refresh and write - m->nm.numWaitThreads = m->waitingThreads.size(); - Memory::WriteStruct(infoAddr, &m->nm); - return 0; -} - bool __KernelUnlockLwMutexForThread(LwMutex *mutex, NativeLwMutexWorkarea &workarea, SceUID threadID, u32 &error, int result) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_LWMUTEX, error); @@ -925,3 +940,64 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) return 0; } + +int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) +{ + u32 error; + LwMutex *m = kernelObjects.Get(uid, error); + if (!m) + return error; + + // Should we crash the thread somehow? + if (!Memory::IsValidAddress(infoPtr)) + return -1; + + if (Memory::Read_U32(infoPtr) != 0) + { + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(m->nm.workareaPtr, &workarea); + + // Refresh and write + m->nm.currentCount = workarea.lockLevel; + m->nm.lockThread = workarea.lockThread == 0 ? -1 : workarea.lockThread; + m->nm.numWaitThreads = m->waitingThreads.size(); + Memory::WriteStruct(infoPtr, &m->nm); + } + return 0; +} + +int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr) +{ + int error = __KernelReferLwMutexStatus(uid, infoPtr); + if (error >= 0) + { + DEBUG_LOG(HLE, "sceKernelReferLwMutexStatusByID(%08x, %08x)", uid, infoPtr); + return error; + } + else + { + ERROR_LOG(HLE, "%08x=sceKernelReferLwMutexStatusByID(%08x, %08x)", error, uid, infoPtr); + return error; + } +} + +int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr) +{ + if (!Memory::IsValidAddress(workareaPtr)) + return -1; + + NativeLwMutexWorkarea workarea; + Memory::ReadStruct(workareaPtr, &workarea); + + int error = __KernelReferLwMutexStatus(workarea.uid, infoPtr); + if (error >= 0) + { + DEBUG_LOG(HLE, "sceKernelReferLwMutexStatus(%08x, %08x)", workareaPtr, infoPtr); + return error; + } + else + { + ERROR_LOG(HLE, "%08x=sceKernelReferLwMutexStatus(%08x, %08x)", error, workareaPtr, infoPtr); + return error; + } +} \ No newline at end of file diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index 259f9f886c9e..9e7bf5797347 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -32,6 +32,8 @@ int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count); int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr); int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr); int sceKernelUnlockLwMutex(u32 workareaPtr, int count); +int sceKernelReferLwMutexStatusByID(SceUID uid, u32 infoPtr); +int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr); void __KernelMutexTimeout(u64 userdata, int cyclesLate); void __KernelLwMutexTimeout(u64 userdata, int cyclesLate); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index ea1f8730ef30..7643cb65c3b3 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -155,22 +155,21 @@ struct ThreadWaitInfo { class MipsCallManager { public: MipsCallManager() : idGen_(0) {} - int add(MipsCall *call) { - int id = genId(); + u32 add(MipsCall *call) { + u32 id = genId(); calls_.insert(std::pair(id, call)); return id; } - MipsCall *get(int id) { + MipsCall *get(u32 id) { return calls_[id]; } - MipsCall *pop(int id) { + MipsCall *pop(u32 id) { MipsCall *temp = calls_[id]; calls_.erase(id); return temp; } void clear() { - std::map::iterator it, end; - for (it = calls_.begin(), end = calls_.end(); it != end; ++it) { + for (auto it = calls_.begin(), end = calls_.end(); it != end; ++it) { delete it->second; } calls_.clear(); @@ -204,10 +203,10 @@ class MipsCallManager { } private: - int genId() { return ++idGen_; } - std::map calls_; + u32 genId() { return ++idGen_; } + std::map calls_; std::vector types_; - int idGen_; + u32 idGen_; }; class ActionAfterMipsCall : public Action @@ -425,7 +424,7 @@ class Thread : public KernelObject std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES]; std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES]; - std::list pendingMipsCalls; + std::list pendingMipsCalls; u32 stackBlock; }; @@ -492,7 +491,7 @@ struct ThreadList } }; -void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter); +void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter); Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr); @@ -795,7 +794,7 @@ void __KernelIdle() CoreTiming::Idle(); // Advance must happen between Idle and Reschedule, so that threads that were waiting for something // that was triggered at the end of the Idle period must get a chance to be scheduled. - CoreTiming::Advance(); + CoreTiming::AdvanceQuick(); // We must've exited a callback? if (__KernelInCallback()) @@ -1034,6 +1033,9 @@ void __KernelLoadContext(ThreadContext *ctx) currentMIPS->fcr0 = ctx->fcr0; currentMIPS->fcr31 = ctx->fcr31; currentMIPS->fpcond = ctx->fpcond; + + // Reset the llBit, the other thread may have touched memory. + currentMIPS->llBit = 0; } u32 __KernelResumeThreadFromWait(SceUID threadID) @@ -1213,7 +1215,7 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason, bo { // TODO: Unless they should be run before deletion? for (int i = 0; i < THREAD_CALLBACK_NUM_TYPES; i++) - readyCallbacksCount -= t->readyCallbacks[i].size(); + readyCallbacksCount -= (int)t->readyCallbacks[i].size(); } return kernelObjects.Destroy(threadID); @@ -1260,7 +1262,7 @@ void __KernelReSchedule(const char *reason) } // Execute any pending events while we're doing scheduling. - CoreTiming::Advance(); + CoreTiming::AdvanceQuick(); if (__IsInInterrupt() || __KernelInCallback()) { reason = "In Interrupt Or Callback"; @@ -1754,7 +1756,7 @@ int sceKernelTerminateDeleteThread(int threadno) } } -int sceKernelTerminateThread(u32 threadID) +int sceKernelTerminateThread(SceUID threadID) { if (threadID != currentThread) { @@ -2253,7 +2255,7 @@ void Thread::setReturnValue(u32 retval) { if (this->GetUID() == currentThread) { if (g_inCbCount) { - int callId = this->currentCallbackId; + u32 callId = this->currentCallbackId; MipsCall *call = mipsCalls.get(callId); if (call) { call->setReturnValue(retval); @@ -2339,7 +2341,7 @@ ThreadWaitInfo Thread::getWaitInfo() void __KernelSwitchContext(Thread *target, const char *reason) { u32 oldPC = 0; - u32 oldUID = 0; + SceUID oldUID = 0; const char *oldName = "(none)"; Thread *cur = __GetCurrentThread(); @@ -2445,7 +2447,7 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, co call->tag = "callAddress"; call->cbId = cbId; - int callId = mipsCalls.add(call); + u32 callId = mipsCalls.add(call); bool called = false; if (!thread || thread == __GetCurrentThread()) { @@ -2472,7 +2474,7 @@ void __KernelDirectMipsCall(u32 entryPoint, Action *afterAction, u32 args[], int __KernelCallAddress(__GetCurrentThread(), entryPoint, afterAction, args, numargs, reschedAfter, 0); } -void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter) +void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) { Thread *cur = __GetCurrentThread(); if (cur == NULL) @@ -2521,7 +2523,7 @@ void __KernelReturnFromMipsCall() return; } - int callId = cur->currentCallbackId; + u32 callId = cur->currentCallbackId; if (currentMIPS->r[MIPS_REG_CALL_ID] != callId) WARN_LOG(HLE, "__KernelReturnFromMipsCall(): s0 is %08x != %08x", currentMIPS->r[MIPS_REG_CALL_ID], callId); @@ -2573,7 +2575,7 @@ bool __KernelExecutePendingMipsCalls(bool reschedAfter) if (__CanExecuteCallbackNow(thread)) { // Pop off the first pending mips call - int callId = thread->pendingMipsCalls.front(); + u32 callId = thread->pendingMipsCalls.front(); thread->pendingMipsCalls.pop_front(); __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter); return true; @@ -2802,10 +2804,13 @@ std::vector GetThreadsInfo() DebugThreadInfo info; info.id = *iter; strncpy(info.name,t->GetName(),KERNELOBJECT_MAX_NAME_LENGTH); - info.name[KERNELOBJECT_MAX_NAME_LENGTH+1] = 0; + info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; info.status = t->nt.status; info.entrypoint = t->nt.entrypoint; - info.curPC = t->context.pc; + if(*iter == currentThread) + info.curPC = currentMIPS->pc; + else + info.curPC = t->context.pc; info.isCurrent = (*iter == currentThread); threadList.push_back(info); } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 1415f2bf005f..228b14816bd8 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -53,7 +53,7 @@ void sceKernelResumeThread(); void sceKernelWakeupThread(); void sceKernelCancelWakeupThread(); int sceKernelTerminateDeleteThread(int threadno); -int sceKernelTerminateThread(u32 threadID); +int sceKernelTerminateThread(SceUID threadID); int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr); void sceKernelGetThreadExitStatus(); u32 sceKernelGetThreadmanIdType(u32); diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index 45ea5ecb3cff..0f7e24f34c21 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -15,24 +15,26 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Core/CoreTiming.h" #include "sceKernel.h" -#include "sceKernelThread.h" +#include "sceKernelInterrupt.h" +#include "sceKernelMemory.h" #include "sceKernelVTimer.h" #include "HLE.h" #include "ChunkFile.h" -// Using ERROR_LOG liberally when this is in development. When done, -// should be changed to DEBUG_LOG wherever applicable. +static int vtimerTimer = -1; +static std::list vtimers; struct NativeVTimer { SceSize size; char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; - int running; - SceKernelSysClock basetime; - SceKernelSysClock curtime; - SceKernelSysClock scheduletime; + int active; + u64 base; + u64 current; + u64 schedule; u32 handlerAddr; - u32 argument; + u32 commonAddr; }; struct VTimer : public KernelObject { @@ -43,198 +45,389 @@ struct VTimer : public KernelObject { virtual void DoState(PointerWrap &p) { p.Do(nvt); + p.Do(memoryPtr); p.DoMarker("VTimer"); } NativeVTimer nvt; + u32 memoryPtr; }; KernelObject *__KernelVTimerObject() { return new VTimer; } -u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { - ERROR_LOG(HLE,"FAKE sceKernelCreateVTimer(%s, %08x)", name, optParamAddr); +u64 __getVTimerRunningTime(VTimer *vt) { + if (!vt->nvt.active) + return 0; - VTimer *vt = new VTimer(); + return cyclesToUs(CoreTiming::GetTicks()) - vt->nvt.base; +} - SceUID uid = kernelObjects.Create(vt); - memset(&vt->nvt, 0, sizeof(vt->nvt)); - if (name) - strncpy(vt->nvt.name, name, 32); - vt->nvt.running = 1; - return uid; //TODO: return timer ID +u64 __getVTimerTime(VTimer* vt) { + return vt->nvt.current + __getVTimerRunningTime(vt); } -u32 sceKernelDeleteVTimer(u32 uid) { - ERROR_LOG(HLE,"FAKE sceKernelDeleteVTimer(%i)", uid); +void __cancelVTimer(SceUID id) { + u32 error; + VTimer *vt = kernelObjects.Get(id, error); + + if (error) + return; + + CoreTiming::UnscheduleEvent(vtimerTimer, id); + vt->nvt.schedule = 0; + vt->nvt.handlerAddr = 0; + vt->nvt.commonAddr = 0; +} + +void __KernelScheduleVTimer(VTimer *vt, u64 schedule) { + CoreTiming::UnscheduleEvent(vtimerTimer, vt->GetUID()); + + vt->nvt.schedule = schedule; + + if (vt->nvt.active == 1) + // this delay makes the test pass, not sure if it's right + CoreTiming::ScheduleEvent(usToCycles(vt->nvt.schedule + 372), vtimerTimer, vt->GetUID()); +} + +void __rescheduleVTimer(SceUID id, u32 delay) { + u32 error; + VTimer *vt = kernelObjects.Get(id, error); + + if (error) + return; + + if (delay < 0) + delay = 100; + + vt->nvt.schedule += delay; + + __KernelScheduleVTimer(vt, vt->nvt.schedule); +} + +class VTimerIntrHandler : public IntrHandler +{ +public: + VTimerIntrHandler() : IntrHandler(PSP_SYSTIMER1_INTR) {} + + virtual bool run(PendingInterrupt &pend) { + u32 error; + SceUID vtimerID = vtimers.front(); + + VTimer *vtimer = kernelObjects.Get(vtimerID, error); + + if (error) + return false; + + if (vtimer->memoryPtr == 0) { + u32 size = 16; + vtimer->memoryPtr = kernelMemory.Alloc(size, true, "VTimer"); + } + + Memory::Write_U64(vtimer->nvt.schedule, vtimer->memoryPtr); + Memory::Write_U64(__getVTimerTime(vtimer), vtimer->memoryPtr + 8); + + currentMIPS->pc = vtimer->nvt.handlerAddr; + currentMIPS->r[MIPS_REG_A0] = vtimer->GetUID(); + currentMIPS->r[MIPS_REG_A1] = vtimer->memoryPtr; + currentMIPS->r[MIPS_REG_A2] = vtimer->memoryPtr + 8; + currentMIPS->r[MIPS_REG_A3] = vtimer->nvt.commonAddr; + + return true; + } + + virtual void handleResult(PendingInterrupt &pend) { + int result = currentMIPS->r[MIPS_REG_V0]; + + int vtimerID = vtimers.front(); + vtimers.pop_front(); + + if (result == 0) + __cancelVTimer(vtimerID); + else + __rescheduleVTimer(vtimerID, result); + } +}; + +void __KernelTriggerVTimer(u64 userdata, int cyclesLate) { + SceUID uid = (SceUID) userdata; u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { - return error; + if (vt) { + vtimers.push_back(uid); + __TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER1_INTR); } - // TODO: Deschedule events here. Might share code with Stop. +} - return kernelObjects.Destroy(uid); +void __KernelVTimerDoState(PointerWrap &p) { + p.Do(vtimerTimer); + p.Do(vtimers); + CoreTiming::RestoreRegisterEvent(vtimerTimer, "VTimer", __KernelTriggerVTimer); + p.DoMarker("sceKernelVTimer"); } -u32 sceKernelStartVTimer(u32 uid) { - ERROR_LOG(HLE,"FAKE sceKernelStartVTimer(%i)", uid); +void __KernelVTimerInit() { + vtimers.clear(); + __RegisterIntrHandler(PSP_SYSTIMER1_INTR, new VTimerIntrHandler()); + vtimerTimer = CoreTiming::RegisterEvent("VTimer", __KernelTriggerVTimer); +} + +u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { + DEBUG_LOG(HLE, "sceKernelCreateVTimer(%s, %08x)", name, optParamAddr); + if (optParamAddr != 0) + WARN_LOG(HLE, "sceKernelCreateVTimer: unsupported options parameter %08x", optParamAddr); + + VTimer *vtimer = new VTimer; + SceUID id = kernelObjects.Create(vtimer); + + memset(&vtimer->nvt, 0, sizeof(NativeVTimer)); + vtimer->nvt.size = sizeof(NativeVTimer); + strncpy(vtimer->nvt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + vtimer->nvt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + vtimer->memoryPtr = 0; + + return id; +} + +u32 sceKernelDeleteVTimer(u32 uid) { + DEBUG_LOG(HLE, "sceKernelDeleteVTimer(%08x)", uid); u32 error; - VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + VTimer* vt = kernelObjects.Get(uid, error); + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelDeleteVTimer(%08x)", error, uid); return error; } - if (vt->nvt.running) { - // Already running - return 1; - } else { - vt->nvt.running = 1; - // TODO: Schedule events etc. - return 0; + + for (std::list::iterator it = vtimers.begin(); it != vtimers.end(); ++it) { + if (*it == vt->GetUID()) { + vtimers.erase(it); + break; + } } + + if (vt->memoryPtr != 0) + kernelMemory.Free(vt->memoryPtr); + + return kernelObjects.Destroy(uid); } -u32 sceKernelStopVTimer(u32 uid) { - ERROR_LOG(HLE,"FAKE sceKernelStartVTimer(%i)", uid); +u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) { + DEBUG_LOG(HLE, "sceKernelGetVTimerBase(%08x, %08x)", uid, baseClockAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelGetVTimerBase(%08x, %08x)", error, uid, baseClockAddr); return error; } - if (vt->nvt.running) { - // Already running - return 0; - } else { - vt->nvt.running = 0; - // TODO: Deschedule events etc. - return 1; - } + + if (Memory::IsValidAddress(baseClockAddr)) + Memory::Write_U64(vt->nvt.base, baseClockAddr); + + return 0; } -u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) { - ERROR_LOG(HLE,"UNIMPL sceKernelSetVTimerHandler(%i, %08x, %08x, %08x)", - uid, scheduleAddr, handlerFuncAddr, commonAddr); +u64 sceKernelGetVTimerBaseWide(u32 uid) { + DEBUG_LOG(HLE, "sceKernelGetVTimerBaseWide(%08x)", uid); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelGetVTimerBaseWide(%08x)", error, uid); return error; } - // TODO - return 0; + + return vt->nvt.base; } -u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) { - ERROR_LOG(HLE,"UNIMPL sceKernelSetVTimerHandlerWide(%i, %llu, %08x, %08x)", - uid, schedule, handlerFuncAddr, commonAddr); +u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) { + DEBUG_LOG(HLE, "sceKernelGetVTimerTime(%08x, %08x)", uid, timeClockAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelGetVTimerTime(%08x, %08x)", error, uid, timeClockAddr); return error; } - // TODO + + u64 time = __getVTimerTime(vt); + if (Memory::IsValidAddress(timeClockAddr)) + Memory::Write_U64(time, timeClockAddr); + return 0; } -u32 sceKernelCancelVTimerHandler(u32 uid) { - ERROR_LOG(HLE,"UNIMPL sceKernelCancelVTimerHandler(%i)", uid); +u64 sceKernelGetVTimerTimeWide(u32 uid) { + DEBUG_LOG(HLE, "sceKernelGetVTimerTimeWide(%08x)", uid); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelGetVTimerTimeWide(%08x)", error, uid); return error; } - // TODO - return 0; + + u64 time = __getVTimerTime(vt); + return time; } -u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) { - ERROR_LOG(HLE,"sceKernelReferVTimerStatus(%i, %08x)", uid, statusAddr); +void __setVTimer(VTimer *vt, u64 time) { + vt->nvt.current = time - __getVTimerRunningTime(vt); +} + +u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) { + DEBUG_LOG(HLE, "sceKernelSetVTimerTime(%08x, %08x)", uid, timeClockAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelSetVTimerTime(%08x, %08x)", error, uid, timeClockAddr); return error; } - // TODO: possibly update time values here? - Memory::WriteStruct(statusAddr, &vt->nvt); + + u64 time = Memory::Read_U64(timeClockAddr); + __setVTimer(vt, time); + return 0; } -u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) { - ERROR_LOG(HLE,"sceKernelGetVTimerBase(%i, %08x)", uid, baseClockAddr); +u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock) { + DEBUG_LOG(HLE, "sceKernelSetVTimerTimeWide(%08x, %llu", uid, timeClock); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelSetVTimerTimeWide(%08x, %llu)", error, uid, timeClock); return error; } - Memory::WriteStruct(baseClockAddr, &vt->nvt.basetime); + + __setVTimer(vt, timeClock); return 0; } -u64 sceKernelGetVTimerBaseWide(u32 uid) { - ERROR_LOG(HLE,"sceKernelGetVTimerWide(%i)", uid); +void __startVTimer(VTimer *vt) { + vt->nvt.active = 1; + vt->nvt.base = cyclesToUs(CoreTiming::GetTicks()); + + if (vt->nvt.schedule != 0 && vt->nvt.handlerAddr != 0) + __KernelScheduleVTimer(vt, vt->nvt.schedule); +} + +u32 sceKernelStartVTimer(u32 uid) { + DEBUG_LOG(HLE, "sceKernelStartVTimer(%08x)", uid); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { - return error; + + if (vt) { + if (vt->nvt.active) + return 1; + + __startVTimer(vt); + return 0; } - // TODO: probably update the timer somehow? - u64 t = vt->nvt.curtime.lo; - t |= (u64)(vt->nvt.curtime.hi) << 32; - return t; + + return error; } -u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) { - ERROR_LOG(HLE,"sceKernelGetVTimerTime(%i, %08x)", uid, timeClockAddr); +void __stopVTimer(VTimer *vt) { + vt->nvt.current += __getVTimerRunningTime(vt); + vt->nvt.active = 0; +} + +u32 sceKernelStopVTimer(u32 uid) { + DEBUG_LOG(HLE, "sceKernelStopVTimer(%08x)", uid); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { - return error; + + if (vt) { + if (vt->nvt.active == 0) + return 0; + + __stopVTimer(vt); + return 1; } - // TODO: probably update the timer somehow? - Memory::WriteStruct(timeClockAddr, &vt->nvt.curtime); - return 0; + + return error; } -u64 sceKernelGetVTimerTimeWide(u32 uid) { - ERROR_LOG(HLE,"sceKernelGetVTimerTimeWide(%i)", uid); +u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) { + DEBUG_LOG(HLE, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", uid, scheduleAddr, handlerFuncAddr, commonAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", error, uid, scheduleAddr, handlerFuncAddr, commonAddr); return error; } - // TODO: probably update the timer somehow? - u64 t = vt->nvt.curtime.lo; - t |= (u64)(vt->nvt.curtime.hi) << 32; - return t; + + u64 schedule = Memory::Read_U64(scheduleAddr); + vt->nvt.handlerAddr = handlerFuncAddr; + vt->nvt.commonAddr = commonAddr; + + __KernelScheduleVTimer(vt, schedule); + + return 0; } -u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) { - ERROR_LOG(HLE,"sceKernelSetVTimerTime(%i, %08x)", uid, timeClockAddr); +u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) { + DEBUG_LOG(HLE, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", uid, schedule, handlerFuncAddr, commonAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", error, uid, schedule, handlerFuncAddr, commonAddr); return error; } - Memory::ReadStruct(timeClockAddr, &vt->nvt.curtime); + + vt->nvt.handlerAddr = handlerFuncAddr; + vt->nvt.commonAddr = commonAddr; + + __KernelScheduleVTimer(vt, schedule); + return 0; } -u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock) { - ERROR_LOG(HLE,"sceKernelSetVTimerTime(%i, %llu)", uid, timeClock); +u32 sceKernelCancelVTimerHandler(u32 uid) { + DEBUG_LOG(HLE, "sceKernelCancelVTimerHandler(%08x)", uid); + + //__cancelVTimer checks if uid is valid + __cancelVTimer(uid); + + return 0; +} + +u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) { + DEBUG_LOG(HLE, "sceKernelReferVTimerStatus(%08x, %08x)", uid, statusAddr); + u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (!vt) { + + if (error) { + WARN_LOG(HLE, "%08x=sceKernelReferVTimerStatus(%08x, %08x)", error, uid, statusAddr); return error; } - vt->nvt.curtime.lo = timeClock & 0xFFFFFFFF; - vt->nvt.curtime.hi = timeClock >> 32; + + if (Memory::IsValidAddress(statusAddr)) + Memory::WriteStruct(statusAddr, &vt->nvt); + return 0; } // Not sure why this is exposed... -void _sceKernelReturnFromTimerHandler() -{ +void _sceKernelReturnFromTimerHandler() { ERROR_LOG(HLE,"_sceKernelReturnFromTimerHandler - should not be called!"); -} +} \ No newline at end of file diff --git a/Core/HLE/sceKernelVTimer.h b/Core/HLE/sceKernelVTimer.h index 9cde7913e517..9153af3c062c 100644 --- a/Core/HLE/sceKernelVTimer.h +++ b/Core/HLE/sceKernelVTimer.h @@ -35,4 +35,6 @@ u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock); // TODO void _sceKernelReturnFromTimerHandler(); +void __KernelVTimerInit(); +void __KernelVTimerDoState(PointerWrap &p); KernelObject *__KernelVTimerObject(); diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index e5a870a01084..5c383dc20d8c 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -23,6 +23,7 @@ #include "sceKernelThread.h" #include "HLE.h" #include "../HW/MediaEngine.h" +#include "../../Core/Config.h" static bool useMediaEngine; @@ -427,6 +428,7 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f } SceMpegRingBuffer ringbuffer; + if(ringbufferAddr != 0){ Memory::ReadStruct(ringbufferAddr, &ringbuffer); if (ringbuffer.packetSize == 0) { ringbuffer.packetsFree = 0; @@ -435,6 +437,7 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f } ringbuffer.mpeg = mpegAddr; Memory::WriteStruct(ringbufferAddr, &ringbuffer); + } // Generate, and write mpeg handle into mpeg data, for some reason int mpegHandle = dataPtr + 0x30; @@ -519,6 +522,10 @@ int sceMpegAvcDecodeMode(u32 mpeg, u32 modeAddr) int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr) { + if (g_Config.bUseMediaEngine == false){ + WARN_LOG(HLE, "Media Engine disabled"); + return -1; + } MpegContext *ctx = getMpegCtx(mpeg); if (!ctx) { WARN_LOG(HLE, "sceMpegQueryStreamOffset(%08x, %08x, %08x): bad mpeg handle", mpeg, bufferAddr, offsetAddr); @@ -833,7 +840,7 @@ int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr) { MpegContext *ctx = getMpegCtx(mpeg); if (!ctx) { - WARN_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %d, %08x, %08x): bad mpeg handle", mpeg, auAddr, bufferAddr, initAddr); + WARN_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x): bad mpeg handle", mpeg, auAddr, bufferAddr, initAddr); return 0; } @@ -1319,6 +1326,33 @@ u32 sceMpegQueryUserdataEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr) return -1; } +u32 sceMpegAvcResourceGetAvcDecTopAddr(u32 mpeg) +{ + ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceGetAvcDecTopAddr(%08x)", mpeg); +// it's just a random address + return 0x12345678; +} + +u32 sceMpegAvcResourceFinish(u32 mpeg) +{ + DEBUG_LOG(HLE,"sceMpegAvcResourceFinish(%08x)", mpeg); + return 0; +} + +u32 sceMpegAvcResourceGetAvcEsBuf(u32 mpeg) +{ + ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceGetAvcEsBuf(%08x)", mpeg); + return 0; +} + +u32 sceMpegAvcResourceInit(u32 mpeg) +{ + ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceInit(%08x)", mpeg); + if (mpeg != 1) { + return ERROR_MPEG_INVALID_VALUE; + } + return 0; +} /* MP3 */ int sceMp3Decode(u32 mp3, u32 outPcmPtr) @@ -1690,6 +1724,10 @@ const HLEFunction sceMpeg[] = {0xC02CF6B5,WrapI_UUU,"sceMpegQueryPcmEsSize"}, {0xC45C99CC,WrapU_UUU,"sceMpegQueryUserdataEsSize"}, {0x234586AE,WrapU_UUI,"sceMpegChangeGetAvcAuMode"}, + {0x63B9536A,WrapU_U,"sceMpegAvcResourceGetAvcDecTopAddr"}, + {0x8160a2fe,WrapU_U,"sceMpegAvcResourceFinish"}, + {0xaf26bb01,WrapU_U,"sceMpegAvcResourceGetAvcEsBuf"}, + {0xfcbdb5ad,WrapU_U,"sceMpegAvcResourceInit"}, }; const HLEFunction sceMp3[] = diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 625aab9720e7..1061004bc131 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -43,6 +43,10 @@ const int numberOfCBPowerSlotsPrivate = 32; static bool volatileMemLocked; static int powerCbSlots[numberOfCBPowerSlots]; +// this should belong here on in CoreTiming? +static int pllFreq = 222; +static int busFreq = 111; + void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); volatileMemLocked = false; @@ -213,9 +217,12 @@ int sceKernelVolatileMemLock(int type, int paddr, int psize) { } -void scePowerSetClockFrequency(u32 cpufreq, u32 busfreq, u32 gpufreq) { +u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) { CoreTiming::SetClockFrequencyMHz(cpufreq); - INFO_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i)", cpufreq, busfreq, gpufreq); + pllFreq = pllfreq; + busFreq = busfreq; + INFO_LOG(HLE,"scePowerSetClockFrequency(%i,%i,%i)", pllfreq, cpufreq, busfreq); + return 0; } u32 scePowerSetCpuClockFrequency(u32 cpufreq) { @@ -225,6 +232,7 @@ u32 scePowerSetCpuClockFrequency(u32 cpufreq) { } u32 scePowerSetBusClockFrequency(u32 busfreq) { + busFreq = busfreq; DEBUG_LOG(HLE,"scePowerSetBusClockFrequency(%i)", busfreq); return 0; } @@ -236,15 +244,18 @@ u32 scePowerGetCpuClockFrequencyInt() { } u32 scePowerGetPllClockFrequencyInt() { - int freq = CoreTiming::GetClockFrequencyMHz() / 2; - INFO_LOG(HLE,"%i=scePowerGetPllClockFrequencyInt()", freq); - return freq; + INFO_LOG(HLE,"%i=scePowerGetPllClockFrequencyInt()", pllFreq); + return pllFreq; +} + +float scePowerGetPllClockFrequencyFloat() { + INFO_LOG(HLE, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq); + return (float) pllFreq; } u32 scePowerGetBusClockFrequencyInt() { - int freq = CoreTiming::GetClockFrequencyMHz() / 2; - INFO_LOG(HLE,"%i=scePowerGetBusClockFrequencyInt()", freq); - return freq; + INFO_LOG(HLE,"%i=scePowerGetBusClockFrequencyInt()", busFreq); + return busFreq; } // a85880d0, unknown name @@ -297,11 +308,11 @@ static const HLEFunction scePower[] = { {0xBD681969,WrapU_V,"scePowerGetBusClockFrequencyInt"}, {0xB1A52C83,0,"scePowerGetCpuClockFrequencyFloat"}, {0x9BADB3EB,0,"scePowerGetBusClockFrequencyFloat"}, - {0x737486F2,WrapV_UUU,"scePowerSetClockFrequency"}, + {0x737486F2,WrapU_UUU,"scePowerSetClockFrequency"}, {0x34f9c463,WrapU_V,"scePowerGetPllClockFrequencyInt"}, - {0xea382a27,0,"scePowerGetPllClockFrequencyFloat"}, - {0xebd177d6,WrapV_UUU,"scePower_driver_EBD177D6"}, //TODO: used in a few places, jpcsp says is the same as scePowerSetClockFrequency - {0x469989ad,WrapV_UUU,"scePower_469989ad"}, // This is also the same as SetClockFrequency + {0xea382a27,WrapF_V,"scePowerGetPllClockFrequencyFloat"}, + {0xebd177d6,WrapU_UUU,"scePower_driver_EBD177D6"}, //TODO: used in a few places, jpcsp says is the same as scePowerSetClockFrequency + {0x469989ad,WrapU_UUU,"scePower_469989ad"}, // This is also the same as SetClockFrequency {0xa85880d0,WrapU_V,"scePower_a85880d0_IsPSPNonFat"}, }; diff --git a/Core/HLE/scePspNpDrm_user.cpp b/Core/HLE/scePspNpDrm_user.cpp new file mode 100644 index 000000000000..a9086911a4a1 --- /dev/null +++ b/Core/HLE/scePspNpDrm_user.cpp @@ -0,0 +1,70 @@ +#include "scePspNpDrm_user.h" + +#include "HLE.h" + +int sceNpDrmSetLicenseeKey() +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmSetLicenseeKey"); + return 0; +} + +int sceNpDrmClearLicenseeKey() +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmClearLicenseeKey"); + return 0; +} + +int sceNpDrmRenameCheck() +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmRenameCheck"); + return 0; +} + +int sceNpDrmEdataSetupKey(u32 edataFd) +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmEdataSetupKey %x", edataFd); + return 0; +} + +int sceNpDrmEdataGetDataSize(u32 edataFd) +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmEdataGetDataSize %x", edataFd); + return 0; +} + +int sceNpDrmOpen() +{ + ERROR_LOG(HLE, "UNIMPL sceNpDrmOpen"); + return 0; +} + +int sceKernelLoadModuleNpDrm() +{ + ERROR_LOG(HLE, "UNIMPL sceKernelLoadModuleNpDrm"); + return 0; +} + +int sceKernelLoadExecNpDrm() +{ + ERROR_LOG(HLE, "UNIMPL sceKernelLoadExecNpDrm"); + return 0; +} + +const HLEFunction sceNpDrm[] = +{ + {0xA1336091, 0, "sceNpDrmSetLicenseeKey"}, + {0x9B745542, 0, "sceNpDrmClearLicenseeKey"}, + {0x275987D1, 0, "sceNpDrmRenameCheck"}, + {0x08d98894, WrapI_U, "sceNpDrmEdataSetupKey"}, + {0x219EF5CC, WrapI_U, "sceNpDrmEdataGetDataSize"}, + {0x2BAA4294, 0, "sceNpDrmOpen"}, + {0xC618D0B1, 0, "sceKernelLoadModuleNpDrm"}, + {0xAA5FC85B, 0, "sceKernelLoadExecNpDrm"}, +}; + +void Register_sceNpDrm() +{ + RegisterModule("sceNpDrm", ARRAY_SIZE(sceNpDrm), sceNpDrm); + RegisterModule("scePspNpDrm_user", ARRAY_SIZE(sceNpDrm), sceNpDrm); +} + \ No newline at end of file diff --git a/Core/HLE/scePspNpDrm_user.h b/Core/HLE/scePspNpDrm_user.h new file mode 100644 index 000000000000..3e59c6aa390d --- /dev/null +++ b/Core/HLE/scePspNpDrm_user.h @@ -0,0 +1,5 @@ +#pragma once + +class PointerWrap; + +void Register_sceNpDrm(); diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 26ad93e6abcd..f92b9cbe81ee 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -28,6 +28,7 @@ #include "HLE.h" #include "../MIPS/MIPS.h" #include "../HW/SasAudio.h" +#include "Core/Reporting.h" #include "sceSas.h" #include "sceKernel.h" @@ -143,6 +144,7 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) { u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop) { INFO_LOG(HLE,"PLEASE REPORT issue #505 sceSasSetVoicePCM(%08x, %i, %08x, %i, %i)", core, voiceNum, pcmAddr, size, loop); + Reporting::ReportMessage("sceSasSetVoicePCM(%x, %i)", core, voiceNum); if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) { WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum); @@ -430,7 +432,7 @@ u32 __sceSasSetVoiceATRAC3(u32 core, int voice, int atrac3Context) { } u32 __sceSasConcatenateATRAC3(u32 core, int voice, u32 atrac3DataAddr, int atrac3DataLength) { - ERROR_LOG(HLE,"UNIMPL __sceSasConcatenateATRAC3(%08x, %i, %i)", core, voice, atrac3DataAddr, atrac3DataLength); + ERROR_LOG(HLE,"UNIMPL __sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voice, atrac3DataAddr, atrac3DataLength); return 0; } diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index 77996f6793df..b54947382d23 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -24,6 +24,8 @@ const int PSP_ERROR_UMD_INVALID_PARAM = 0x80010016; +const u64 MICRO_DELAY_ACTIVATE = 4000; + #define UMD_NOT_PRESENT 0x01 #define UMD_PRESENT 0x02 #define UMD_CHANGED 0x04 @@ -31,12 +33,27 @@ const int PSP_ERROR_UMD_INVALID_PARAM = 0x80010016; #define UMD_READY 0x10 #define UMD_READABLE 0x20 +struct UmdWaitingThread +{ + SceUID threadID; + u32 stat; + + inline static UmdWaitingThread Make(SceUID threadID, u32 stat) + { + UmdWaitingThread th; + th.threadID = threadID; + th.stat = stat; + return th; + } +}; static u8 umdActivated = 1; static u32 umdStatus = 0; static u32 umdErrorStat = 0; static int driveCBId = -1; -static int umdStatTimer = -1; +static int umdStatTimeoutEvent = -1; +static int umdStatChangeEvent = -1; +static std::vector umdWaitingThreads; #define PSP_UMD_TYPE_GAME 0x10 @@ -49,10 +66,12 @@ struct PspUmdInfo { }; void __UmdStatTimeout(u64 userdata, int cyclesLate); +void __UmdStatChange(u64 userdata, int cyclesLate); void __UmdInit() { - umdStatTimer = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout); + umdStatTimeoutEvent = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout); + umdStatChangeEvent = CoreTiming::RegisterEvent("UmdChange", __UmdStatChange); umdActivated = 1; umdStatus = 0; umdErrorStat = 0; @@ -65,8 +84,11 @@ void __UmdDoState(PointerWrap &p) p.Do(umdStatus); p.Do(umdErrorStat); p.Do(driveCBId); - p.Do(umdStatTimer); - CoreTiming::RestoreRegisterEvent(umdStatTimer, "UmdTimeout", __UmdStatTimeout); + p.Do(umdStatTimeoutEvent); + CoreTiming::RestoreRegisterEvent(umdStatTimeoutEvent, "UmdTimeout", __UmdStatTimeout); + p.Do(umdStatChangeEvent); + CoreTiming::RestoreRegisterEvent(umdStatChangeEvent, "UmdChange", __UmdStatChange); + p.Do(umdWaitingThreads); p.DoMarker("sceUmd"); } @@ -83,24 +105,55 @@ u8 __KernelUmdGetState() return state; } -void __KernelUmdActivate() +void __UmdStatChange(u64 userdata, int cyclesLate) { - umdActivated = 1; + // TODO: Why not a bool anyway? + umdActivated = userdata & 0xFF; + + // Wake anyone waiting on this. + for (size_t i = 0; i < umdWaitingThreads.size(); ++i) + { + UmdWaitingThread &info = umdWaitingThreads[i]; + + u32 error; + SceUID waitID = __KernelGetWaitID(info.threadID, WAITTYPE_UMD, error); + bool keep = false; + if (waitID == 1) + { + if ((info.stat & __KernelUmdGetState()) != 0) + __KernelResumeThreadFromWait(info.threadID, 0); + // Only if they are still waiting do we keep them in the list. + else + keep = true; + } + + if (!keep) + umdWaitingThreads.erase(umdWaitingThreads.begin() + i--); + } } -void __KernelUmdDeactivate() +void __KernelUmdActivate() { - umdActivated = 0; + u32 notifyArg = UMD_PRESENT | UMD_READABLE; + __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); + + // Don't activate immediately, take time to "spin up." + CoreTiming::RemoveAllEvents(umdStatChangeEvent); + CoreTiming::ScheduleEvent(usToCycles(MICRO_DELAY_ACTIVATE), umdStatChangeEvent, 1); } -// typedef int (*UmdCallback)(int unknown, int event); +void __KernelUmdDeactivate() +{ + u32 notifyArg = UMD_PRESENT | UMD_READY; + __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); + CoreTiming::RemoveAllEvents(umdStatChangeEvent); + __UmdStatChange(0, 0); +} -//int sceUmdCheckMedium(int a); int sceUmdCheckMedium() { - DEBUG_LOG(HLE,"1=sceUmdCheckMedium(?)"); - //ignore PARAM(0) + DEBUG_LOG(HLE, "1=sceUmdCheckMedium()"); return 1; //non-zero: disc in drive } @@ -128,7 +181,6 @@ int sceUmdActivate(u32 unknown, const char *name) if (unknown < 1 || unknown > 2) return PSP_ERROR_UMD_INVALID_PARAM; - bool changed = umdActivated == 0; __KernelUmdActivate(); if (unknown == 1) @@ -140,9 +192,6 @@ int sceUmdActivate(u32 unknown, const char *name) ERROR_LOG(HLE, "UNTESTED 0=sceUmdActivate(%d, %s)", unknown, name); } - u32 notifyArg = UMD_PRESENT | UMD_READABLE; - __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); - return 0; } @@ -152,7 +201,6 @@ int sceUmdDeactivate(u32 unknown, const char *name) if (unknown > 18) return PSP_ERROR_UMD_INVALID_PARAM; - bool changed = umdActivated != 0; __KernelUmdDeactivate(); if (unknown == 1) @@ -164,9 +212,6 @@ int sceUmdDeactivate(u32 unknown, const char *name) ERROR_LOG(HLE, "UNTESTED 0=sceUmdDeactivate(%d, %s)", unknown, name); } - u32 notifyArg = UMD_PRESENT | UMD_READY; - __KernelNotifyCallbackType(THREAD_CALLBACK_UMD, -1, notifyArg); - return 0; } @@ -191,9 +236,9 @@ u32 sceUmdRegisterUMDCallBack(u32 cbId) return retVal; } -u32 sceUmdUnRegisterUMDCallBack(u32 cbId) +int sceUmdUnRegisterUMDCallBack(int cbId) { - u32 retVal; + int retVal; if (cbId != driveCBId) retVal = PSP_ERROR_UMD_INVALID_PARAM; @@ -225,6 +270,12 @@ void __UmdStatTimeout(u64 userdata, int cyclesLate) // Assuming it's still waiting. if (waitID == 1) __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + + for (size_t i = 0; i < umdWaitingThreads.size(); ++i) + { + if (umdWaitingThreads[i].threadID == threadID) + umdWaitingThreads.erase(umdWaitingThreads.begin() + i--); + } } void __UmdWaitStat(u32 timeout) @@ -235,7 +286,7 @@ void __UmdWaitStat(u32 timeout) else if (timeout <= 215) timeout = 250; - CoreTiming::ScheduleEvent(usToCycles((int) timeout), umdStatTimer, __KernelGetCurThread()); + CoreTiming::ScheduleEvent(usToCycles((int) timeout), umdStatTimeoutEvent, __KernelGetCurThread()); } /** @@ -250,7 +301,10 @@ int sceUmdWaitDriveStat(u32 stat) DEBUG_LOG(HLE,"0=sceUmdWaitDriveStat(stat = %08x)", stat); if ((stat & __KernelUmdGetState()) == 0) + { + umdWaitingThreads.push_back(UmdWaitingThread::Make(__KernelGetCurThread(), stat)); __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited"); + } return 0; } @@ -262,7 +316,8 @@ int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) if ((stat & __KernelUmdGetState()) == 0) { __UmdWaitStat(timeout); - __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited"); + umdWaitingThreads.push_back(UmdWaitingThread::Make(__KernelGetCurThread(), stat)); + __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited with timer"); } else hleReSchedule("umd stat waited with timer"); @@ -272,14 +327,7 @@ int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) { - if (driveCBId != -1) - { - DEBUG_LOG(HLE,"0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout); - } - else - { - WARN_LOG(HLE, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d) without callback", stat, timeout); - } + DEBUG_LOG(HLE,"0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout); hleCheckCurrentCallbacks(); if ((stat & __KernelUmdGetState()) == 0) @@ -288,6 +336,7 @@ int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) timeout = 8000; __UmdWaitStat(timeout); + umdWaitingThreads.push_back(UmdWaitingThread::Make(__KernelGetCurThread(), stat)); __KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, true, "umd stat waited"); } else @@ -324,8 +373,6 @@ u32 sceUmdReplacePermit() return 0; } - - const HLEFunction sceUmdUser[] = { {0xC6183D47,WrapI_UC,"sceUmdActivate"}, @@ -340,7 +387,7 @@ const HLEFunction sceUmdUser[] = {0x20628E6F,&WrapU_V,"sceUmdGetErrorStat"}, {0x340B7686,WrapU_U,"sceUmdGetDiscInfo"}, {0xAEE7404D,&WrapU_U,"sceUmdRegisterUMDCallBack"}, - {0xBD2BDE07,&WrapU_U,"sceUmdUnRegisterUMDCallBack"}, + {0xBD2BDE07,&WrapI_I,"sceUmdUnRegisterUMDCallBack"}, {0x87533940,WrapU_V,"sceUmdReplaceProhibit"}, {0x87533940,WrapU_V,"sceUmdReplacePermit"}, }; diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 97f1b8837a2d..ce79551865dd 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -88,6 +88,11 @@ void VagDecoder::GetSamples(s16 *outSamples, int numSamples) { return; } u8 *readp = Memory::GetPointer(read_); + if (!readp) + { + WARN_LOG(HLE, "Bad VAG samples address?"); + return; + } u8 *origp = readp; for (int i = 0; i < numSamples; i++) { if (curSample == 28) { @@ -548,8 +553,8 @@ static int getExpCurveAt(int index, int duration) { } ADSREnvelope::ADSREnvelope() - : steps_(0), - state_(STATE_OFF), + : state_(STATE_OFF), + steps_(0), height_(0) { memset(this, 0, sizeof(*this)); } diff --git a/Core/Host.h b/Core/Host.h index 65d17824895c..01573a1e655a 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -46,7 +46,6 @@ class Host virtual void InitGL() = 0; virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() = 0; virtual void InitSound(PMixer *mixer) = 0; @@ -65,8 +64,8 @@ class Host virtual bool GpuStep() { return false; } virtual void SendGPUStart() {} - virtual void SendGPUWait(u32 cmd) {} - virtual void SetGPUStep(bool value, int flag = 0) {} + virtual void SendGPUWait(u32 cmd, u32 addr, void* data) {} + virtual void SetGPUStep(bool value, int flag = 0, int data = 0) {} virtual void NextGPUStep() {} // Used for headless. diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index d7d1e4b9072e..2ec66f05be40 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -117,6 +117,9 @@ void Jit::GenerateFixedCode() MOVI2R(R10, (u32)mips_); MOVI2R(R9, (u32)GetBlockCache()->GetCodePointers()); + MovFromPC(R0); + outerLoopPCInR0 = GetCodePtr(); + MovToPC(R0); outerLoop = GetCodePtr(); QuickCallFunction(R0, (void *)&CoreTiming::Advance); FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time @@ -133,6 +136,9 @@ void Jit::GenerateFixedCode() FixupBranch badCoreState = B_CC(CC_NEQ); FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time + dispatcherPCInR0 = GetCodePtr(); + MovToPC(R0); + // At this point : flags = EQ. Fine for the next check, no need to jump over it. dispatcher = GetCodePtr(); @@ -149,7 +155,7 @@ void Jit::GenerateFixedCode() // MOV(R0, R13); // QuickCallFunction(R1, (void *)&ShowPC); - LDR(R0, R10, offsetof(MIPSState, pc)); + LDR(R0, CTXREG, offsetof(MIPSState, pc)); BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF LDR(R0, R11, R0, true, true); AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. @@ -158,7 +164,7 @@ void Jit::GenerateFixedCode() SetCC(CC_EQ); // IDEA - we have 26 bits, why not just use offsets from base of code? // Another idea: Shift the bloc number left by two in the op, this would let us do - // LDR(R0, R9, R0, true, true); here, replacing the two next instructions. + // LDR(R0, R9, R0, true, true); here, replacing the next instructions. ADD(R0, R9, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); @@ -191,6 +197,7 @@ void Jit::GenerateFixedCode() // INFO_LOG(HLE, "END OF THE DISASM ========================"); // Don't forget to zap the instruction cache! + FlushLitPool(); FlushIcache(); } diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 9fdc796c2d19..b0020a1a9a46 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "ArmJit.h" +#include "Common/CPUDetect.h" using namespace MIPSAnalyst; #define _RS ((op>>21) & 0x1F) @@ -28,7 +29,12 @@ using namespace MIPSAnalyst; #define _POS ((op>>6 ) & 0x1F) #define _SIZE ((op>>11 ) & 0x1F) -#define DISABLE Comp_Generic(op); return; +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. + +//#define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +#define CONDITIONAL_DISABLE ; +#define DISABLE { Comp_Generic(op); return; } namespace MIPSComp { @@ -55,6 +61,7 @@ namespace MIPSComp void Jit::Comp_IType(u32 op) { + CONDITIONAL_DISABLE; s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension u32 uimm = op & 0xFFFF; u32 suimm = (u32)(s32)simm; @@ -145,20 +152,47 @@ namespace MIPSComp break; } } - + + void Jit::Comp_RType2(u32 op) + { + CONDITIONAL_DISABLE; + int rs = _RS; + int rd = _RD; + + // Don't change $zr. + if (rd == 0) + return; + + switch (op & 63) + { + case 22: //clz + gpr.MapDirtyIn(rd, rs); + CLZ(gpr.R(rd), gpr.R(rs)); + break; + case 23: //clo + gpr.MapDirtyIn(rd, rs); + RSB(R0, gpr.R(rs), Operand2(0)); + CLZ(gpr.R(rd), R0); + break; + default: + DISABLE; + } + } + void Jit::Comp_RType3(u32 op) { + CONDITIONAL_DISABLE; int rt = _RT; int rs = _RS; int rd = _RD; switch (op & 63) { - //case 10: if (!R(rt)) R(rd) = R(rs); break; //movz - //case 11: if (R(rt)) R(rd) = R(rs); break; //movn + //case 10: if (!R(rt)) R(rd) = R(rs); break; //movz + //case 11: if (R(rt)) R(rd) = R(rs); break; //movn - // case 32: //R(rd) = R(rs) + R(rt); break; //add - case 33: //R(rd) = R(rs) + R(rt); break; //addu + // case 32: //R(rd) = R(rs) + R(rt); break; //add + case 33: //R(rd) = R(rs) + R(rt); break; //addu // Some optimized special cases if (rs == 0) { gpr.MapDirtyIn(rd, rt); @@ -171,28 +205,32 @@ namespace MIPSComp ADD(gpr.R(rd), gpr.R(rs), gpr.R(rt)); } break; - case 34: //R(rd) = R(rs) - R(rt); break; //sub + case 34: //R(rd) = R(rs) - R(rt); break; //sub case 35: gpr.MapDirtyInIn(rd, rs, rt); SUB(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 36: //R(rd) = R(rs) & R(rt); break; //and + case 36: //R(rd) = R(rs) & R(rt); break; //and gpr.MapDirtyInIn(rd, rs, rt); AND(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 37: //R(rd) = R(rs) | R(rt); break; //or + case 37: //R(rd) = R(rs) | R(rt); break; //or gpr.MapDirtyInIn(rd, rs, rt); ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor + case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor gpr.MapDirtyInIn(rd, rs, rt); EOR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 39: // R(rd) = ~(R(rs) | R(rt)); //nor + case 39: // R(rd) = ~(R(rs) | R(rt)); break; //nor gpr.MapDirtyInIn(rd, rs, rt); - ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); - MVN(gpr.R(rd), gpr.R(rd)); + if (rt == 0) { + MVN(gpr.R(rd), gpr.R(rs)); + } else { + ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); + MVN(gpr.R(rd), gpr.R(rd)); + } break; case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt @@ -205,7 +243,7 @@ namespace MIPSComp SetCC(CC_AL); break; - case 43: //R(rd) = R(rs) < R(rt); break; //sltu + case 43: //R(rd) = R(rs) < R(rt); break; //sltu gpr.MapDirtyInIn(rd, rs, rt); CMP(gpr.R(rs), gpr.R(rt)); SetCC(CC_LO); @@ -215,11 +253,25 @@ namespace MIPSComp SetCC(CC_AL); break; - // case 44: R(rd) = (R(rs) > R(rt)) ? R(rs) : R(rt); break; //max - // CMP(a,b); CMOVLT(a,b) + case 44: //R(rd) = max(R(rs), R(rt); break; //max + gpr.MapDirtyInIn(rd, rs, rt); + CMP(gpr.R(rs), gpr.R(rt)); + SetCC(CC_GT); + MOV(gpr.R(rd), gpr.R(rs)); + SetCC(CC_LE); + MOV(gpr.R(rd), gpr.R(rt)); + SetCC(CC_AL); + break; - // case 45: R(rd) = (R(rs) < R(rt)) ? R(rs) : R(rt); break; //min - // CMP(a,b); CMOVGT(a,b) + case 45: //R(rd) = min(R(rs), R(rt)); break; //min + gpr.MapDirtyInIn(rd, rs, rt); + CMP(gpr.R(rs), gpr.R(rt)); + SetCC(CC_LT); + MOV(gpr.R(rd), gpr.R(rs)); + SetCC(CC_GE); + MOV(gpr.R(rd), gpr.R(rt)); + SetCC(CC_AL); + break; default: // gpr.UnlockAll(); @@ -238,76 +290,171 @@ namespace MIPSComp MOV(gpr.R(rd), Operand2(sa, shiftType, gpr.R(rt))); } - // "over-shifts" work the same as on x86 - only bottom 5 bits are used to get the shift value - /* - void Jit::CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)) + void Jit::CompShiftVar(u32 op, ArmGen::ShiftType shiftType) { int rd = _RD; int rt = _RT; int rs = _RS; - gpr.FlushLockX(ECX); - gpr.Lock(rd, rt, rs); - gpr.BindToRegister(rd, true, true); - if (rd != rt) - MOV(32, gpr.R(rd), gpr.R(rt)); - MOV(32, R(ECX), gpr.R(rs)); // Only ECX can be used for variable shifts. - AND(32, R(ECX), Imm32(0x1f)); - (this->*shift)(32, gpr.R(rd), R(ECX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); + if (gpr.IsImm(rs)) + { + gpr.MapDirtyIn(rd, rt); + int sa = gpr.GetImm(rs) & 0x1F; + MOV(gpr.R(rd), Operand2(sa, shiftType, gpr.R(rt))); + return; + } + gpr.MapDirtyInIn(rd, rs, rt); + AND(R0, gpr.R(rs), Operand2(0x1F)); + MOV(gpr.R(rd), Operand2(gpr.R(rt), shiftType, R0)); } -*/ + void Jit::Comp_ShiftType(u32 op) { - // WARNIGN : ROTR + CONDITIONAL_DISABLE; + int rs = _RS; + int fd = _FD; + // WARNING : ROTR switch (op & 0x3f) { - case 0: CompShiftImm(op, ST_LSL); break; - case 2: CompShiftImm(op, ST_LSR); break; // srl - case 3: CompShiftImm(op, ST_ASR); break; // sra - - // case 4: CompShiftVar(op, &XEmitter::SHL); break; // R(rd) = R(rt) << R(rs); break; //sllv - // case 6: CompShiftVar(op, &XEmitter::SHR); break; // R(rd) = R(rt) >> R(rs); break; //srlv - // case 7: CompShiftVar(op, &XEmitter::SAR); break; // R(rd) = ((s32)R(rt)) >> R(rs); break; //srav + case 0: CompShiftImm(op, ST_LSL); break; //sll + case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR); break; //srl + case 3: CompShiftImm(op, ST_ASR); break; //sra + case 4: CompShiftVar(op, ST_LSL); break; //sllv + case 6: CompShiftVar(op, rs == 1 ? ST_ROR : ST_LSR); break; //srlv + case 7: CompShiftVar(op, ST_ASR); break; //srav default: Comp_Generic(op); - //_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); break; } } void Jit::Comp_Special3(u32 op) { - // ext, ins - DISABLE; + CONDITIONAL_DISABLE; + + bool useUBFXandBFI = false; + + if (!cpu_info.bArmV7) { + // useUBFXandBFI = true; + } + + int rs = _RS; + int rt = _RT; + + int pos = _POS; + int size = _SIZE + 1; + u32 mask = 0xFFFFFFFFUL >> (32 - size); + + // Don't change $zr. + if (rt == 0) + return; + + switch (op & 0x3f) + { + case 0x0: //ext + if (gpr.IsImm(rs)) + { + gpr.SetImm(rt, (gpr.GetImm(rs) >> pos) & mask); + return; + } + + gpr.MapDirtyIn(rt, rs, false); + if (useUBFXandBFI) { + UBFX(gpr.R(rt), gpr.R(rs), pos, size); + } else { + MOV(gpr.R(rt), Operand2(pos, ST_LSR, gpr.R(rs))); + ANDI2R(gpr.R(rt), gpr.R(rt), mask, R0); + } + break; + + case 0x4: //ins + { + DISABLE; + u32 sourcemask = mask >> pos; + u32 destmask = ~(sourcemask << pos); + if (gpr.IsImm(rs)) + { + u32 inserted = (gpr.GetImm(rs) & sourcemask) << pos; + if (gpr.IsImm(rt)) + { + gpr.SetImm(rt, (gpr.GetImm(rt) & destmask) | inserted); + return; + } + + gpr.MapReg(rt, MAP_DIRTY); + ANDI2R(gpr.R(rt), gpr.R(rt), destmask, R0); + ORI2R(gpr.R(rt), gpr.R(rt), inserted, R0); + } + else + { + if (useUBFXandBFI) { + gpr.MapDirtyIn(rt, rs, false); + BFI(gpr.R(rt), gpr.R(rs), pos, size); + } else { + gpr.MapDirtyIn(rt, rs, false); + ANDI2R(R0, gpr.R(rs), sourcemask, R1); + MOV(R0, Operand2(pos, ST_LSL, R0)); + ANDI2R(gpr.R(rt), gpr.R(rt), destmask, R1); + ORR(gpr.R(rt), gpr.R(rt), R0); + } + } + } + break; + } } void Jit::Comp_Allegrex(u32 op) { - DISABLE + CONDITIONAL_DISABLE; int rt = _RT; int rd = _RD; + // Don't change $zr. + if (rd == 0) + return; + switch ((op >> 6) & 31) { case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); - /* - gpr.Lock(rd, rt); - gpr.BindToRegister(rd, true, true); - MOV(32, R(EAX), gpr.R(rt)); // work around the byte-register addressing problem - MOVSX(32, 8, gpr.RX(rd), R(EAX)); - gpr.UnlockAll();*/ + if (gpr.IsImm(rt)) + { + gpr.SetImm(rd, (s32)(s8)(u8)gpr.GetImm(rt)); + return; + } + gpr.MapDirtyIn(rd, rt); + SXTB(gpr.R(rd), gpr.R(rt)); break; case 24: // seh - /* - gpr.Lock(rd, rt); - gpr.BindToRegister(rd, true, true); - MOVSX(32, 16, gpr.RX(rd), gpr.R(rt)); - gpr.UnlockAll();*/ + if (gpr.IsImm(rt)) + { + gpr.SetImm(rd, (s32)(s16)(u16)gpr.GetImm(rt)); + return; + } + gpr.MapDirtyIn(rd, rt); + SXTH(gpr.R(rd), gpr.R(rt)); break; - + case 20: //bitrev + if (gpr.IsImm(rt)) + { + // http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + u32 v = gpr.GetImm(rt); + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // odd<->even + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // pair<->pair + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // nibb<->nibb + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // byte<->byte + v = ( v >> 16 ) | ( v << 16); // hword<->hword + gpr.SetImm(rd, v); + return; + } + + if (cpu_info.bArmV7) { + gpr.MapDirtyIn(rd, rt); + RBIT(gpr.R(rd), gpr.R(rt)); + } else { + Comp_Generic(op); + } + break; default: Comp_Generic(op); return; @@ -316,7 +463,7 @@ namespace MIPSComp void Jit::Comp_MulDivType(u32 op) { - // DISABLE; + CONDITIONAL_DISABLE; int rt = _RT; int rs = _RS; int rd = _RD; @@ -351,6 +498,67 @@ namespace MIPSComp case 25: //multu (2nd) lo,hi = unsigned mul (rs * rt) gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt); UMULL(gpr.R(MIPSREG_LO), gpr.R(MIPSREG_HI), gpr.R(rs), gpr.R(rt)); + break; + + case 26: //div + DISABLE; + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt); + if (cpu_info.bIDIVa) + { + SDIV(gpr.R(MIPSREG_LO), gpr.R(rs), gpr.R(rt)); + MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO)); + SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0)); + } else { + VMOV(S0, gpr.R(rs)); + VMOV(S1, gpr.R(rt)); + VCVT(D0, S0, TO_FLOAT | IS_SIGNED); + VCVT(D1, S1, TO_FLOAT | IS_SIGNED); + VDIV(D0, D0, D1); + VCVT(gpr.R(MIPSREG_LO), D0, TO_INT | IS_SIGNED); + MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO)); + SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0)); + } + break; + + case 27: //divu + DISABLE; + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt); + if (cpu_info.bIDIVa) + { + UDIV(gpr.R(MIPSREG_LO), gpr.R(rs), gpr.R(rt)); + MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO)); + SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0)); + } else { + VMOV(S0, gpr.R(rs)); + VMOV(S1, gpr.R(rt)); + VCVT(D0, S0, TO_FLOAT); + VCVT(D1, S1, TO_FLOAT); + VDIV(D0, D0, D1); + VCVT(gpr.R(MIPSREG_LO), D0, TO_INT); + MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO)); + SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0)); + } + break; + + case 28: //madd + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt, false); + SMLAL(gpr.R(MIPSREG_LO), gpr.R(MIPSREG_HI), gpr.R(rs), gpr.R(rt)); + break; + + case 29: //maddu + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt, false); + UMLAL(gpr.R(MIPSREG_LO), gpr.R(MIPSREG_HI), gpr.R(rs), gpr.R(rt)); + break; + + case 46: // msub + DISABLE; + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt, false); + break; + + case 47: // msubu + DISABLE; + gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt, false); + break; default: DISABLE; diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index f3b149c6c7cb..bd5eed47c6f9 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -77,10 +77,7 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) } else { - gpr.SpillLock(rs, rt); - gpr.MapReg(rs); - gpr.MapReg(rt); - gpr.ReleaseSpillLocks(); + gpr.MapInIn(rs, rt); CMP(gpr.R(rs), gpr.R(rt)); } @@ -149,9 +146,8 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool andLink, bool likely // Take the branch if (andLink) { - ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram MOVI2R(R0, js.compilerPC + 8); - STR(R1, R0); + STR(CTXREG, R0, MIPS_REG_RA * 4); } WriteExit(targetAddr, 0); @@ -343,9 +339,8 @@ void Jit::Comp_Jump(u32 op) break; case 3: //jal - ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram MOVI2R(R0, js.compilerPC + 8); - STR(R1, R0); + STR(CTXREG, R0, MIPS_REG_RA * 4); WriteExit(targetAddr, 0); break; @@ -392,9 +387,8 @@ void Jit::Comp_JumpReg(u32 op) case 8: //jr break; case 9: //jalr - ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram MOVI2R(R0, js.compilerPC + 8); - STR(R1, R0); + STR(CTXREG, R0, MIPS_REG_RA * 4); break; default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); diff --git a/Core/MIPS/ARM/ArmCompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp index 78a1aa39f059..15e581ac8e44 100644 --- a/Core/MIPS/ARM/ArmCompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -19,95 +19,85 @@ #include "ArmJit.h" #include "ArmRegCache.h" -#define _RS ((op>>21) & 0x1F) -#define _RT ((op>>16) & 0x1F) -#define _RD ((op>>11) & 0x1F) -#define _FS ((op>>11) & 0x1F) -#define _FT ((op>>16) & 0x1F) -#define _FD ((op>>6 ) & 0x1F) -#define _POS ((op>>6 ) & 0x1F) -#define _SIZE ((op>>11 ) & 0x1F) +#define _RS ((op>>21) & 0x1F) +#define _RT ((op>>16) & 0x1F) +#define _RD ((op>>11) & 0x1F) +#define _FS ((op>>11) & 0x1F) +#define _FT ((op>>16) & 0x1F) +#define _FD ((op>>6 ) & 0x1F) +#define _POS ((op>>6 ) & 0x1F) +#define _SIZE ((op>>11) & 0x1F) +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. + +//#define CONDITIONAL_DISABLE { Comp_Generic(op); return; } #define CONDITIONAL_DISABLE ; -#define DISABLE Comp_Generic(op); return; +#define DISABLE { Comp_Generic(op); return; } namespace MIPSComp { - /* -void Jit::CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters) -{ - int ft = _FT; - int fs = _FS; - int fd = _FD; - fpr.Lock(ft, fs, fd); - - if (false && fs == fd) - { - fpr.BindToRegister(fd, true, true); - (this->*arith)(fpr.RX(fd), fpr.R(ft)); - } - else - { - MOVSS(XMM0, fpr.R(fs)); - MOVSS(XMM1, fpr.R(ft)); - fpr.BindToRegister(fd, true, true); - (this->*arith)(XMM0, R(XMM1)); - MOVSS(fpr.RX(fd), R(XMM0)); - } - fpr.UnlockAll(); -} -*/ - - void Jit::Comp_FPU3op(u32 op) { - DISABLE + CONDITIONAL_DISABLE; + + int ft = _FT; + int fs = _FS; + int fd = _FD; + + fpr.MapDirtyInIn(fd, fs, ft); switch (op & 0x3f) { - //case 0: CompFPTriArith(op, &XEmitter::ADDSS, false); break; //F(fd) = F(fs) + F(ft); //add - //case 1: CompFPTriArith(op, &XEmitter::SUBSS, true); break; //F(fd) = F(fs) - F(ft); //sub - //case 2: CompFPTriArith(op, &XEmitter::MULSS, false); break; //F(fd) = F(fs) * F(ft); //mul - //case 3: CompFPTriArith(op, &XEmitter::DIVSS, true); break; //F(fd) = F(fs) / F(ft); //div + case 0: VADD(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) + F(ft); //add + case 1: VSUB(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) - F(ft); //sub + case 2: VMUL(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) * F(ft); //mul + case 3: VDIV(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) / F(ft); //div default: - Comp_Generic(op); + DISABLE; return; } } +extern int logBlocks; + void Jit::Comp_FPULS(u32 op) { - DISABLE + CONDITIONAL_DISABLE; - s32 offset = (s16)(op&0xFFFF); - int ft = ((op>>16)&0x1f); + s32 offset = (s16)(op & 0xFFFF); + int ft = _FT; int rs = _RS; // u32 addr = R(rs) + offset; - + // logBlocks = 1; switch(op >> 26) { - /* case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1 - gpr.Lock(rs); - fpr.Lock(ft); - fpr.BindToRegister(ft, false, true); - MOV(32, R(EAX), gpr.R(rs)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVSS(fpr.RX(ft), MDisp(EAX, (u32)Memory::base + offset)); - gpr.UnlockAll(); - fpr.UnlockAll(); + fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY); + if (gpr.IsImm(rs)) { + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; + MOVI2R(R0, addr + (u32)Memory::base); + } else { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, offset); + ADD(R0, R0, R11); + } + VLDR(fpr.R(ft), R0, 0); break; + case 57: //Memory::Write_U32(FI(ft), addr); break; //swc1 - gpr.Lock(rs); - fpr.Lock(ft); - fpr.BindToRegister(ft, true, false); - MOV(32, R(EAX), gpr.R(rs)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVSS(MDisp(EAX, (u32)Memory::base + offset), fpr.RX(ft)); - gpr.UnlockAll(); - fpr.UnlockAll(); - break; - */ + fpr.MapReg(ft); + if (gpr.IsImm(rs)) { + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; + MOVI2R(R0, addr + (u32)Memory::base); + } else { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, offset); + ADD(R0, R0, R11); + } + VSTR(fpr.R(ft), R0, 0); + break; + default: Comp_Generic(op); return; @@ -115,84 +105,144 @@ void Jit::Comp_FPULS(u32 op) } void Jit::Comp_FPUComp(u32 op) { - DISABLE; + CONDITIONAL_DISABLE; + int opc = op & 0xF; + if (opc >= 8) opc -= 8; // alias + if (opc == 0)//f, sf (signalling false) + { + MOVI2R(R0, 0); + STR(CTXREG, R0, offsetof(MIPSState, fpcond)); + return; + } + + int fs = _FS; + int ft = _FT; + fpr.MapInIn(fs, ft); + VCMP(fpr.R(fs), fpr.R(ft), false); + VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags). + switch(opc) + { + case 1: // un, ngle (unordered) + SetCC(CC_VS); + MOVI2R(R0, 1); + SetCC(CC_VC); + break; + case 2: // eq, seq (equal, ordered) + SetCC(CC_EQ); + MOVI2R(R0, 1); + SetCC(CC_NEQ); + break; + case 3: // ueq, ngl (equal, unordered) + SetCC(CC_EQ); + MOVI2R(R0, 1); + SetCC(CC_NEQ); + MOVI2R(R0, 0); + SetCC(CC_VC); + break; + case 4: // olt, lt (less than, ordered) + SetCC(CC_LO); + MOVI2R(R0, 1); + SetCC(CC_HS); + break; + case 5: // ult, nge (less than, unordered) + SetCC(CC_LT); + MOVI2R(R0, 1); + SetCC(CC_GE); + break; + case 6: // ole, le (less equal, ordered) + SetCC(CC_LS); + MOVI2R(R0, 1); + SetCC(CC_HI); + break; + case 7: // ule, ngt (less equal, unordered) + SetCC(CC_LE); + MOVI2R(R0, 1); + SetCC(CC_GT); + break; + default: + Comp_Generic(op); + return; + } + MOVI2R(R0, 0); + SetCC(CC_AL); + STR(CTXREG, R0, offsetof(MIPSState, fpcond)); } void Jit::Comp_FPU2op(u32 op) { - DISABLE + CONDITIONAL_DISABLE; + int fs = _FS; int fd = _FD; + // logBlocks = 1; switch (op & 0x3f) { - /* - case 5: //F(fd) = fabsf(F(fs)); break; //abs - fpr.Lock(fd, fs); - fpr.BindToRegister(fd, fd == fs, true); - MOVSS(fpr.RX(fd), fpr.R(fs)); - PAND(fpr.RX(fd), M((void *)ssNoSignMask)); - fpr.UnlockAll(); - break; - - case 6: //F(fd) = F(fs); break; //mov - if (fd != fs) { - fpr.Lock(fd, fs); - fpr.BindToRegister(fd, fd == fs, true); - MOVSS(fpr.RX(fd), fpr.R(fs)); - fpr.UnlockAll(); - } + case 4: //F(fd) = sqrtf(F(fs)); break; //sqrt + fpr.MapDirtyIn(fd, fs); + VSQRT(fpr.R(fd), fpr.R(fs)); break; - - case 7: //F(fd) = -F(fs); break; //neg - fpr.Lock(fd, fs); - fpr.BindToRegister(fd, fd == fs, true); - MOVSS(fpr.RX(fd), fpr.R(fs)); - PXOR(fpr.RX(fd), M((void *)ssSignBits2)); - fpr.UnlockAll(); + case 5: //F(fd) = fabsf(F(fs)); break; //abs + fpr.MapDirtyIn(fd, fs); + VABS(fpr.R(fd), fpr.R(fs)); + break; + case 6: //F(fd) = F(fs); break; //mov + fpr.MapDirtyIn(fd, fs); + VMOV(fpr.R(fd), fpr.R(fs)); + break; + case 7: //F(fd) = -F(fs); break; //neg + fpr.MapDirtyIn(fd, fs); + VNEG(fpr.R(fd), fpr.R(fs)); break; - case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s - - case 4: //F(fd) = sqrtf(F(fs)); break; //sqrt - Comp_Generic(op); - return; - - case 13: //FsI(fd) = F(fs)>=0 ? (int)floorf(F(fs)) : (int)ceilf(F(fs)); break;//trunc.w.s - fpr.Lock(fs, fd); - fpr.StoreFromRegister(fd); - CVTTSS2SI(EAX, fpr.R(fs)); - MOV(32, fpr.R(fd), R(EAX)); - fpr.UnlockAll(); + fpr.MapDirtyIn(fd, fs); + VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED); + break; + case 13: //FsI(fd) = Rto0(F(fs))); break; //trunc.w.s + fpr.MapDirtyIn(fd, fs); + VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED | ROUND_TO_ZERO); + break; + case 14: //FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s + fpr.MapDirtyIn(fd, fs); + MOVI2R(R0, 0x3F000000); // 0.5f + VMOV(S0, R0); + VADD(S0,fpr.R(fs),S0); + VCVT(fpr.R(fd), S0, TO_INT | IS_SIGNED); + break; + case 15: //FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s + fpr.MapDirtyIn(fd, fs); + MOVI2R(R0, 0x3F000000); // 0.5f + VMOV(S0, R0); + VSUB(S0,fpr.R(fs),S0); + VCVT(fpr.R(fd), S0, TO_INT | IS_SIGNED); + break; + case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w + fpr.MapDirtyIn(fd, fs); + VCVT(fpr.R(fd), fpr.R(fs), TO_FLOAT | IS_SIGNED); + break; + case 36: //FsI(fd) = (int) F(fs); break; //cvt.w.s + fpr.MapDirtyIn(fd, fs); + VCVT(fpr.R(fd), fpr.R(fs), TO_INT | ROUND_TO_ZERO); break; - - case 14: //FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s - case 15: //FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s - case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w - case 36: //FsI(fd) = (int) F(fs); break; //cvt.w.s - */ default: - Comp_Generic(op); - return; + DISABLE; } } void Jit::Comp_mxc1(u32 op) { - DISABLE + CONDITIONAL_DISABLE; + int fs = _FS; int rt = _RT; switch((op >> 21) & 0x1f) { - /* case 0: // R(rt) = FI(fs); break; //mfc1 - // Cross move! slightly tricky - fpr.StoreFromRegister(fs); - gpr.Lock(rt); - gpr.BindToRegister(rt, false, true); - MOV(32, gpr.R(rt), fpr.R(fs)); - gpr.UnlockAll(); + // Let's just go through RAM for now. + fpr.FlushR(fs); + gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT); + LDR(gpr.R(rt), CTXREG, fpr.GetMipsRegOffset(fs)); return; case 2: // R(rt) = currentMIPS->ReadFCR(fs); break; //cfc1 @@ -200,14 +250,12 @@ void Jit::Comp_mxc1(u32 op) return; case 4: //FI(fs) = R(rt); break; //mtc1 - // Cross move! slightly tricky - gpr.StoreFromRegister(rt); - fpr.Lock(fs); - fpr.BindToRegister(fs, false, true); - MOVSS(fpr.RX(fs), gpr.R(rt)); - fpr.UnlockAll(); + // Let's just go through RAM for now. + gpr.FlushR(rt); + fpr.MapReg(fs, MAP_DIRTY | MAP_NOINIT); + VLDR(fpr.R(fs), CTXREG, gpr.GetMipsRegOffset(rt)); return; - */ + case 6: //currentMIPS->WriteFCR(fs, R(rt)); break; //ctc1 Comp_Generic(op); return; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index fff481a29591..37e153fcc1fc 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -52,11 +52,15 @@ #define _POS ((op>>6 ) & 0x1F) #define _SIZE ((op>>11 ) & 0x1F) -#define DISABLE Comp_Generic(op); return; +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. + +// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +#define CONDITIONAL_DISABLE ; +#define DISABLE { Comp_Generic(op); return; } namespace MIPSComp { - void Jit::SetR0ToEffectiveAddress(int rs, s16 offset) { Operand2 op2; if (offset) { @@ -84,7 +88,9 @@ namespace MIPSComp void Jit::Comp_ITypeMem(u32 op) { + CONDITIONAL_DISABLE; int offset = (signed short)(op&0xFFFF); + bool shifter = false, load = false; int rt = _RT; int rs = _RS; int o = op>>26; @@ -92,103 +98,105 @@ namespace MIPSComp // Don't load anything into $zr return; } - switch (o) - { - case 37: //R(rt) = ReadMem16(addr); break; //lhu - Comp_Generic(op); - return; - case 35: //R(rt) = ReadMem32(addr); //lw - case 36: //R(rt) = ReadMem8 (addr); break; //lbu - if (g_Config.bFastMemory) { - if (gpr.IsImm(rs)) { - // We can compute the full address at compile time. Kickass. - u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; - gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); // must be OK even if rs == rt since we have the value from imm already. - MOVI2R(R0, addr); - } else { - gpr.MapDirtyIn(rt, rs); - SetR0ToEffectiveAddress(rs, offset); - } - if (o == 35) { - LDR(gpr.R(rt), R11, R0, true, true); - } else if (o == 36) { - ADD(R0, R0, R11); // TODO: Merge with next instruction - LDRB(gpr.R(rt), R0); - } - } else { - Comp_Generic(op); + // Optimisation: Combine to single unaligned load/store + switch(o) + { + case 34: //lwl + case 38: //lwr + load = true; + case 42: //swl + case 46: //swr + { + int left = (o == 34 || o == 42) ? 1 : -1; + u32 nextOp = Memory::Read_Instruction(js.compilerPC + 4); + // Find a matching shift in opposite direction with opposite offset. + u32 desiredOp = ((op + left* (4 << 26)) & 0xFFFF0000) + (offset - left*3); + if (!js.inDelaySlot && nextOp == desiredOp) + { + EatInstruction(nextOp); + nextOp = ((load ? 35 : 43) << 26) | (nextOp & 0x3FFFFFF); //lw, sw + Comp_ITypeMem(nextOp); return; } + shifter = true; + } + default: break; + } - case 41: //WriteMem16(addr, R(rt)); break; //sh - Comp_Generic(op); - return; - + switch (o) + { + case 32: //lb + case 33: //lh + case 34: //lwl + case 35: //lw + case 36: //lbu + case 37: //lhu + case 38: //lwr + load = true; case 40: //sb - case 43: //WriteMem32(addr, R(rt)); break; //sw + case 41: //sh + case 42: //swl + case 43: //sw + case 46: //swr if (g_Config.bFastMemory) { + int shift = 0; + if (shifter) + { + shift = (offset & 3) << 3; + offset &= 0xfffffffc; + } if (gpr.IsImm(rs)) { // We can compute the full address at compile time. Kickass. u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; - gpr.MapReg(rt); + // Must be OK even if rs == rt since we have the value from imm already. + gpr.MapReg(rt, load ? MAP_NOINIT | MAP_DIRTY : 0); MOVI2R(R0, addr); } else { - gpr.MapInIn(rt, rs); + load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs); SetR0ToEffectiveAddress(rs, offset); } - if (o == 43) { + switch (o) + { + // Load + case 34: + AND(gpr.R(rt), gpr.R(rt), 0x00ffffff >> shift); + LDR(R0, R11, R0, true, true); + ORR(gpr.R(rt), gpr.R(rt), Operand2(24 - shift, ST_LSL, R0)); + break; + case 38: + AND(gpr.R(rt), gpr.R(rt), 0xffffff00 << (24 - shift)); + LDR(R0, R11, R0, true, true); + ORR(gpr.R(rt), gpr.R(rt), Operand2(shift, ST_LSR, R0)); + break; + case 35: LDR (gpr.R(rt), R11, R0, true, true); break; + case 37: LDRH (gpr.R(rt), R11, R0, true, true); break; + case 33: LDRSH(gpr.R(rt), R11, R0, true, true); break; + case 36: LDRB (gpr.R(rt), R11, R0, true, true); break; + case 32: LDRSB(gpr.R(rt), R11, R0, true, true); break; + // Store + case 42: + LSR(gpr.R(rt), gpr.R(rt), 24-shift); STR(R0, gpr.R(rt), R11, true, true); - } else if (o == 40) { - ADD(R0, R0, R11); - STRB(R0, gpr.R(rt)); + AND(R0, R0, 0xffffff00 << shift); + ORR(R0, R0, gpr.R(rt)); + break; + case 46: + LSL(gpr.R(rt), gpr.R(rt), shift); + STR(R0, gpr.R(rt), R11, true, true); + AND(R0, R0, 0x00ffffff >> (24 - shift)); + ORR(R0, R0, gpr.R(rt)); + break; + case 43: STR (R0, gpr.R(rt), R11, true, true); break; + case 41: STRH (R0, gpr.R(rt), R11, true, true); break; + case 40: STRB (R0, gpr.R(rt), R11, true, true); break; } } else { Comp_Generic(op); return; } break; - // break; - /* - case 34: //lwl - { - Crash(); - //u32 shift = (addr & 3) << 3; - //u32 mem = ReadMem32(addr & 0xfffffffc); - //R(rt) = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) ); - } - break; - - case 38: //lwr - { - Crash(); - //u32 shift = (addr & 3) << 3; - //u32 mem = ReadMem32(addr & 0xfffffffc); - - //R(rt) = ( u32(rt) & (0xffffff00 << (24 - shift)) ) | ( mem >> shift ); - } - break; - - case 42: //swl - { - Crash(); - //u32 shift = (addr & 3) << 3; - //u32 mem = ReadMem32(addr & 0xfffffffc); - //WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) >> (24 - shift) ) ) | - // ( mem & (0xffffff00 << shift) )); - } - break; - case 46: //swr - { - Crash(); - // u32 shift = (addr & 3) << 3; - // u32 mem = ReadMem32(addr & 0xfffffffc); -// -// WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) << shift ) | -// (mem & (0x00ffffff >> (24 - shift)) ) ) ); - } - break;*/ default: Comp_Generic(op); return ; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 34bfa0802955..8a0dc74d26a2 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -1,5 +1,23 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + #include "../../MemMap.h" #include "../MIPSAnalyst.h" +#include "Core/Reporting.h" #include "ArmJit.h" #include "ArmRegCache.h" @@ -14,42 +32,619 @@ #define _POS ((op>>6 ) & 0x1F) #define _SIZE ((op>>11 ) & 0x1F) -// #define CONDITIONAL_DISABLE Comp_Generic(op); return; +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. + +// #define CONDITIONAL_DISABLE { fpr.ReleaseSpillLocks(); Comp_Generic(op); return; } #define CONDITIONAL_DISABLE ; -#define DISABLE Comp_Generic(op); return; +#define DISABLE { fpr.ReleaseSpillLocks(); Comp_Generic(op); return; } namespace MIPSComp { + // Vector regs can overlap in all sorts of swizzled ways. + // This does allow a single overlap in sregs[i]. + bool IsOverlapSafeAllowS(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[] = NULL) + { + for (int i = 0; i < sn; ++i) + { + if (sregs[i] == dreg && i != di) + return false; + } + for (int i = 0; i < tn; ++i) + { + if (tregs[i] == dreg) + return false; + } + + // Hurray, no overlap, we can write directly. + return true; + } + + bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[] = NULL) + { + return IsOverlapSafeAllowS(dreg, di, sn, sregs, tn, tregs) && sregs[di] != dreg; + } + void Jit::Comp_VPFX(u32 op) { - DISABLE; + CONDITIONAL_DISABLE; + + int data = op & 0xFFFFF; + int regnum = (op >> 24) & 3; + switch (regnum) { + case 0: // S + js.prefixS = data; + js.prefixSFlag = ArmJitState::PREFIX_KNOWN_DIRTY; + break; + case 1: // T + js.prefixT = data; + js.prefixTFlag = ArmJitState::PREFIX_KNOWN_DIRTY; + break; + case 2: // D + js.prefixD = data; + js.prefixDFlag = ArmJitState::PREFIX_KNOWN_DIRTY; + break; + } + } + + void Jit::ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz) { + if (prefix == 0xE4) return; + + int n = GetNumVectorElements(sz); + u8 origV[4]; + static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f}; + + for (int i = 0; i < n; i++) + origV[i] = vregs[i]; + + for (int i = 0; i < n; i++) + { + int regnum = (prefix >> (i*2)) & 3; + int abs = (prefix >> (8+i)) & 1; + int negate = (prefix >> (16+i)) & 1; + int constants = (prefix >> (12+i)) & 1; + + // Unchanged, hurray. + if (!constants && regnum == i && !abs && !negate) + continue; + + // This puts the value into a temp reg, so we won't write the modified value back. + vregs[i] = fpr.GetTempV(); + fpr.MapRegV(vregs[i], MAP_NOINIT | MAP_DIRTY); + + if (!constants) { + // Prefix may say "z, z, z, z" but if this is a pair, we force to x. + // TODO: But some ops seem to use const 0 instead? + if (regnum >= n) { + ERROR_LOG(CPU, "Invalid VFPU swizzle: %08x / %d", prefix, sz); + Reporting::ReportMessage("Invalid VFPU swizzle: %08x / %d", prefix, sz); + regnum = 0; + } + + if (abs) { + VABS(fpr.V(vregs[i]), fpr.V(origV[regnum])); + } else { + VMOV(fpr.V(vregs[i]), fpr.V(origV[regnum])); + } + } else { + // TODO: There is VMOV s, imm on ARM, that can generate some of these constants. Not 1/3 or 1/6 though. + MOVI2F(fpr.V(vregs[i]), constantArray[regnum + (abs<<2)], R0); + } + + // TODO: This can be integrated into the VABS / VMOV above, and also the constants. + if (negate) + VNEG(fpr.V(vregs[i]), fpr.V(vregs[i])); + + // TODO: This probably means it will swap out soon, inefficiently... + fpr.ReleaseSpillLockV(vregs[i]); + } + } + + void Jit::GetVectorRegsPrefixD(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixDFlag & ArmJitState::PREFIX_KNOWN); + + GetVectorRegs(regs, sz, vectorReg); + if (js.prefixD == 0) + return; + + int n = GetNumVectorElements(sz); + for (int i = 0; i < n; i++) { + // Hopefully this is rare, we'll just write it into a reg we drop. + if (js.VfpuWriteMask(i)) + regs[i] = fpr.GetTempV(); + } + } + + void Jit::ApplyPrefixD(const u8 *vregs, VectorSize sz) { + _assert_(js.prefixDFlag & ArmJitState::PREFIX_KNOWN); + if (!js.prefixD) return; + + int n = GetNumVectorElements(sz); + for (int i = 0; i < n; i++) { + if (js.VfpuWriteMask(i)) + continue; + + int sat = (js.prefixD >> (i * 2)) & 3; + if (sat == 1) { + fpr.MapRegV(vregs[i], MAP_DIRTY); + // ARGH this is a pain - no MIN/MAX in non-NEON VFP! + // NEON does have min/max though so this should only be a fallback. + MOVI2F(S0, 0.0, R0); + MOVI2F(S1, 1.0, R0); + VCMP(fpr.V(vregs[i]), S1); + VMRS_APSR(); + SetCC(CC_GE); + VMOV(fpr.V(vregs[i]), S1); + FixupBranch skip = B(); + SetCC(CC_AL); + VCMP(fpr.V(vregs[i]), S0); + VMRS_APSR(); + SetCC(CC_LE); + VMOV(fpr.V(vregs[i]), S0); + SetCC(CC_AL); + SetJumpTarget(skip); + } else if (sat == 3) { + fpr.MapRegV(vregs[i], MAP_DIRTY); + MOVI2F(S0, -1.0, R0); + MOVI2F(S1, 1.0, R0); + VCMP(fpr.V(vregs[i]), S1); + VMRS_APSR(); + SetCC(CC_GE); + VMOV(fpr.V(vregs[i]), S1); + FixupBranch skip = B(); + SetCC(CC_AL); + VCMP(fpr.V(vregs[i]), S0); + VMRS_APSR(); + SetCC(CC_LE); + VMOV(fpr.V(vregs[i]), S0); + SetCC(CC_AL); + SetJumpTarget(skip); + } + } + } + + void Jit::Comp_SV(u32 op) { + CONDITIONAL_DISABLE; + + s32 imm = (signed short)(op&0xFFFC); + int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); + int rs = _RS; + + switch (op >> 26) + { + case 50: //lv.s // VI(vt) = Memory::Read_U32(addr); + { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, imm); + ADD(R0, R0, R11); + fpr.MapRegV(vt, MAP_DIRTY | MAP_NOINIT); + fpr.ReleaseSpillLocks(); + VLDR(fpr.V(vt), R0, 0); + } + break; + + case 58: //sv.s // Memory::Write_U32(VI(vt), addr); + { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, imm); + ADD(R0, R0, R11); + fpr.MapRegV(vt); + fpr.ReleaseSpillLocks(); + VSTR(fpr.V(vt), R0, 0); + } + break; + + + default: + DISABLE; + } } void Jit::Comp_SVQ(u32 op) { - DISABLE; + CONDITIONAL_DISABLE; + + int imm = (signed short)(op&0xFFFC); + int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); + int rs = _RS; + + switch (op >> 26) + { + case 54: //lv.q + { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, imm); + ADD(R0, R0, R11); + + u8 vregs[4]; + GetVectorRegs(vregs, V_Quad, vt); + fpr.MapRegsV(vregs, V_Quad, MAP_DIRTY | MAP_NOINIT); + fpr.ReleaseSpillLocks(); + for (int i = 0; i < 4; i++) + VLDR(fpr.V(vregs[i]), R0, i * 4); + } + break; + + case 62: //sv.q + { + gpr.MapReg(rs); + SetR0ToEffectiveAddress(rs, imm); + ADD(R0, R0, R11); + + u8 vregs[4]; + GetVectorRegs(vregs, V_Quad, vt); + fpr.MapRegsV(vregs, V_Quad, 0); + fpr.ReleaseSpillLocks(); + for (int i = 0; i < 4; i++) + VSTR(fpr.V(vregs[i]), R0, i * 4); + } + break; + + default: + DISABLE; + break; + } + } + + void Jit::Comp_VVectorInit(u32 op) + { + CONDITIONAL_DISABLE; + + // WARNING: No prefix support! + if (js.MayHavePrefix()) { + Comp_Generic(op); + js.EatPrefix(); + return; + } + + switch ((op >> 16) & 0xF) + { + case 6: // v=zeros; break; //vzero + MOVI2F(S0, 0.0f, R0); + break; + case 7: // v=ones; break; //vone + MOVI2F(S0, 1.0f, R0); + break; + default: + DISABLE; + break; + } + + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + u8 dregs[4]; + GetVectorRegsPrefixD(dregs, sz, _VD); + fpr.MapRegsV(dregs, sz, MAP_NOINIT | MAP_DIRTY); + + for (int i = 0; i < n; ++i) + VMOV(fpr.V(dregs[i]), S0); + + ApplyPrefixD(dregs, sz); + fpr.ReleaseSpillLocks(); } void Jit::Comp_VDot(u32 op) { - DISABLE; + // DISABLE; + CONDITIONAL_DISABLE; + // WARNING: No prefix support! + if (js.MayHavePrefix()) { + Comp_Generic(op); + js.EatPrefix(); + return; + } + + int vd = _VD; + int vs = _VS; + int vt = _VT; + VectorSize sz = GetVecSize(op); + + // TODO: Force read one of them into regs? probably not. + u8 sregs[4], tregs[4]; + GetVectorRegs(sregs, sz, vs); + GetVectorRegs(tregs, sz, vt); + + // TODO: applyprefixST here somehow (shuffle, etc...) + fpr.MapRegsV(sregs, sz, 0); + fpr.MapRegsV(tregs, sz, 0); + VMUL(S0, fpr.V(sregs[0]), fpr.V(tregs[0])); + + int n = GetNumVectorElements(sz); + for (int i = 1; i < n; i++) { + // sum += s[i]*t[i]; + VMLA(S0, fpr.V(sregs[i]), fpr.V(tregs[i])); + } + fpr.ReleaseSpillLocks(); + + fpr.MapRegV(vd, MAP_NOINIT | MAP_DIRTY); + + // TODO: applyprefixD here somehow (write mask etc..) + VMOV(fpr.V(vd), S0); + + fpr.ReleaseSpillLocks(); + + js.EatPrefix(); } void Jit::Comp_VecDo3(u32 op) { + CONDITIONAL_DISABLE; DISABLE; + // WARNING: No prefix support! + if (js.MayHavePrefix()) + { + Comp_Generic(op); + js.EatPrefix(); + return; + } + + int vd = _VD; + int vs = _VS; + int vt = _VT; + + void (ARMXEmitter::*triop)(ARMReg, ARMReg, ARMReg) = NULL; + switch (op >> 26) + { + case 24: //VFPU0 + switch ((op >> 23)&7) + { + case 0: // d[i] = s[i] + t[i]; break; //vadd + triop = &ARMXEmitter::VADD; + break; + case 1: // d[i] = s[i] - t[i]; break; //vsub + triop = &ARMXEmitter::VSUB; + break; + case 7: // d[i] = s[i] / t[i]; break; //vdiv + triop = &ARMXEmitter::VDIV; + break; + } + break; + case 25: //VFPU1 + switch ((op >> 23)&7) + { + case 0: // d[i] = s[i] * t[i]; break; //vmul + triop = &ARMXEmitter::VMUL; + break; + } + break; + } + + if (!triop) { + DISABLE; + } + + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + u8 sregs[4], tregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixT(tregs, sz, _VT); + GetVectorRegsPrefixD(dregs, sz, _VD); + + MIPSReg tempregs[4]; + for (int i = 0; i < n; i++) { + if (!IsOverlapSafeAllowS(dregs[i], i, n, sregs, n, tregs)) { + tempregs[i] = fpr.GetTempV(); + } else { + fpr.MapRegV(dregs[i], (dregs[i] == sregs[i] || dregs[i] == tregs[i] ? 0 : MAP_NOINIT) | MAP_DIRTY); + tempregs[i] = dregs[i]; + } + } + + for (int i = 0; i < n; i++) { + fpr.SpillLockV(sregs[i]); + fpr.SpillLockV(tregs[i]); + fpr.MapRegV(sregs[i]); + fpr.MapRegV(tregs[i]); + fpr.MapRegV(tempregs[i]); + (this->*triop)(fpr.V(tempregs[i]), fpr.V(sregs[i]), fpr.V(tregs[i])); + fpr.ReleaseSpillLockV(sregs[i]); + fpr.ReleaseSpillLockV(tregs[i]); + } + + fpr.MapRegsV(dregs, sz, MAP_DIRTY); + for (int i = 0; i < n; i++) { + if (dregs[i] != tempregs[i]) + VMOV(fpr.V(dregs[i]), fpr.V(tempregs[i])); + } + ApplyPrefixD(dregs, sz); + + fpr.ReleaseSpillLocks(); + + js.EatPrefix(); } + void Jit::Comp_VV2Op(u32 op) { + CONDITIONAL_DISABLE; + + DISABLE; + + if (js.HasUnknownPrefix()) + DISABLE; + + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + u8 sregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixD(dregs, sz, _VD); + + ARMReg tempxregs[4]; + for (int i = 0; i < n; ++i) + { + if (!IsOverlapSafeAllowS(dregs[i], i, n, sregs)) + { + int reg = fpr.GetTempV(); + fpr.MapRegV(reg, MAP_NOINIT | MAP_DIRTY); + fpr.SpillLockV(reg); + tempxregs[i] = fpr.V(reg); + } + else + { + fpr.MapRegV(dregs[i], (dregs[i] == sregs[i] ? 0 : MAP_NOINIT) | MAP_DIRTY); + fpr.SpillLockV(dregs[i]); + tempxregs[i] = fpr.V(dregs[i]); + } + } + + // Warning: sregs[i] and tempxregs[i] may be the same reg. + // Helps for vmov, hurts for vrcp, etc. + for (int i = 0; i < n; ++i) + { + switch ((op >> 16) & 0x1f) + { + case 0: // d[i] = s[i]; break; //vmov + // Probably for swizzle. + VMOV(tempxregs[i], fpr.V(sregs[i])); + break; + case 1: // d[i] = fabsf(s[i]); break; //vabs + //if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + VABS(tempxregs[i], fpr.V(sregs[i])); + break; + case 2: // d[i] = -s[i]; break; //vneg + VNEG(tempxregs[i], fpr.V(sregs[i])); + break; + case 4: // if (s[i] < 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 + DISABLE; + break; + case 5: // if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 + DISABLE; + break; + case 16: // d[i] = 1.0f / s[i]; break; //vrcp + MOVI2R(R0, 0x3F800000); // 1.0f + VMOV(S0, R0); + VDIV(tempxregs[i], S0, fpr.V(sregs[i])); + break; + case 17: // d[i] = 1.0f / sqrtf(s[i]); break; //vrsq + MOVI2R(R0, 0x3F800000); // 1.0f + VMOV(S0, R0); + VSQRT(S1, fpr.V(sregs[i])); + VDIV(tempxregs[i], S0, S1); + break; + case 18: // d[i] = sinf((float)M_PI_2 * s[i]); break; //vsin + DISABLE; + break; + case 19: // d[i] = cosf((float)M_PI_2 * s[i]); break; //vcos + DISABLE; + break; + case 20: // d[i] = powf(2.0f, s[i]); break; //vexp2 + DISABLE; + break; + case 21: // d[i] = logf(s[i])/log(2.0f); break; //vlog2 + DISABLE; + break; + case 22: // d[i] = sqrtf(s[i]); break; //vsqrt + VSQRT(tempxregs[i], fpr.V(sregs[i])); + VABS(tempxregs[i], tempxregs[i]); + break; + case 23: // d[i] = asinf(s[i] * (float)M_2_PI); break; //vasin + DISABLE; + break; + case 24: // d[i] = -1.0f / s[i]; break; // vnrcp + MOVI2R(R0, 0x80000000 | 0x3F800000); // -1.0f + VMOV(S0, R0); + VDIV(tempxregs[i], S0, fpr.V(sregs[i])); + break; + case 26: // d[i] = -sinf((float)M_PI_2 * s[i]); break; // vnsin + DISABLE; + break; + case 28: // d[i] = 1.0f / expf(s[i] * (float)M_LOG2E); break; // vrexp2 + DISABLE; + break; + } + } + + fpr.MapRegsV(dregs, sz, MAP_NOINIT | MAP_DIRTY); + for (int i = 0; i < n; ++i) + { + VMOV(fpr.V(dregs[i]), tempxregs[i]); + } + + ApplyPrefixD(dregs, sz); + + fpr.ReleaseSpillLocks(); + } + void Jit::Comp_Mftv(u32 op) { - DISABLE; + CONDITIONAL_DISABLE; + + int imm = op & 0xFF; + int rt = _RT; + switch ((op >> 21) & 0x1f) + { + case 3: //mfv / mfvc + // rt = 0, imm = 255 appears to be used as a CPU interlock by some games. + if (rt != 0) { + if (imm < 128) { //R(rt) = VI(imm); + fpr.FlushV(imm); + gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); + LDR(gpr.R(rt), CTXREG, fpr.GetMipsRegOffsetV(imm)); + } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc + DISABLE; + // In case we have a saved prefix. + //FlushPrefixV(); + //gpr.BindToRegister(rt, false, true); + //MOV(32, gpr.R(rt), M(¤tMIPS->vfpuCtrl[imm - 128])); + } else { + //ERROR - maybe need to make this value too an "interlock" value? + _dbg_assert_msg_(CPU,0,"mfv - invalid register"); + } + } + break; + + case 7: //mtv + if (imm < 128) { + gpr.FlushR(rt); + fpr.MapRegV(imm, MAP_DIRTY | MAP_NOINIT); + VLDR(fpr.V(imm), CTXREG, gpr.GetMipsRegOffset(rt)); + } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc //currentMIPS->vfpuCtrl[imm - 128] = R(rt); + DISABLE; + //gpr.BindToRegister(rt, true, false); + //MOV(32, M(¤tMIPS->vfpuCtrl[imm - 128]), gpr.R(rt)); + + // TODO: Optimization if rt is Imm? + //if (imm - 128 == VFPU_CTRL_SPREFIX) { + //js.prefixSFlag = JitState::PREFIX_UNKNOWN; + //} else if (imm - 128 == VFPU_CTRL_TPREFIX) { + // js.prefixTFlag = JitState::PREFIX_UNKNOWN; + //} else if (imm - 128 == VFPU_CTRL_DPREFIX) { + // js.prefixDFlag = JitState::PREFIX_UNKNOWN; + //} + } else { + //ERROR + _dbg_assert_msg_(CPU,0,"mtv - invalid register"); + } + break; + + default: + DISABLE; + } } - void Jit::Comp_SV(u32 op) { + void Jit::Comp_Vmtvc(u32 op) { DISABLE; + + int vs = _VS; + int imm = op & 0xFF; + if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { + fpr.MapRegV(vs, 0); + ADD(R0, CTXREG, offsetof(MIPSState, vfpuCtrl[0]) + (imm - 128) * 4); + VSTR(fpr.V(vs), R0, 0); + fpr.ReleaseSpillLocks(); + + if (imm - 128 == VFPU_CTRL_SPREFIX) { + js.prefixSFlag = ArmJitState::PREFIX_UNKNOWN; + } else if (imm - 128 == VFPU_CTRL_TPREFIX) { + js.prefixTFlag = ArmJitState::PREFIX_UNKNOWN; + } else if (imm - 128 == VFPU_CTRL_DPREFIX) { + js.prefixDFlag = ArmJitState::PREFIX_UNKNOWN; + } + } } - void Jit::Comp_Vmtvc(u32 op) { + void Jit::Comp_Vmmov(u32 op) { DISABLE; } diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 7933a76f5ac1..ae80235f825b 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/ChunkFile.h" #include "../../Core.h" #include "../../CoreTiming.h" #include "../MIPS.h" @@ -24,6 +25,7 @@ #include "ArmRegCache.h" #include "ArmJit.h" +#include "CPUDetect.h" #include "../../../ext/disarm.h" @@ -54,19 +56,53 @@ void DisassembleArm(const u8 *data, int size) { namespace MIPSComp { -Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips) +Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), fpr(mips), mips_(mips) { blocks.Init(); gpr.SetEmitter(this); - //fpr.SetEmitter(this); + fpr.SetEmitter(this); AllocCodeSpace(1024 * 1024 * 16); // 32MB is the absolute max because that's what an ARM branch instruction can reach, backwards and forwards. GenerateFixedCode(); + + js.startDefaultPrefix = true; +} + +void Jit::DoState(PointerWrap &p) +{ + p.Do(js.startDefaultPrefix); + p.DoMarker("Jit"); + FlushPrefixV(); } void Jit::FlushAll() { gpr.FlushAll(); - //fpr.Flush(FLUSH_ALL); + fpr.FlushAll(); + FlushPrefixV(); +} + +void Jit::FlushPrefixV() +{ + if ((js.prefixSFlag & ArmJitState::PREFIX_DIRTY) != 0) + { + MOVI2R(R0, js.prefixS); + STR(CTXREG, R0, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_SPREFIX])); + js.prefixSFlag = (ArmJitState::PrefixState) (js.prefixSFlag & ~ArmJitState::PREFIX_DIRTY); + } + + if ((js.prefixTFlag & ArmJitState::PREFIX_DIRTY) != 0) + { + MOVI2R(R0, js.prefixT); + STR(CTXREG, R0, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_TPREFIX])); + js.prefixTFlag = (ArmJitState::PrefixState) (js.prefixTFlag & ~ArmJitState::PREFIX_DIRTY); + } + + if ((js.prefixDFlag & ArmJitState::PREFIX_DIRTY) != 0) + { + MOVI2R(R0, js.prefixD); + STR(CTXREG, R0, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_DPREFIX])); + js.prefixDFlag = (ArmJitState::PrefixState) (js.prefixDFlag & ~ArmJitState::PREFIX_DIRTY); + } } void Jit::ClearCache() @@ -88,6 +124,16 @@ void Jit::CompileAt(u32 addr) MIPSCompileOp(op); } +void Jit::EatInstruction(u32 op) +{ + u32 info = MIPSGetInfo(op); + _dbg_assert_msg_(JIT, !(info & DELAYSLOT), "Never eat a branch op."); + _dbg_assert_msg_(JIT, !js.inDelaySlot, "Never eat an instruction inside a delayslot."); + + js.compilerPC += 4; + js.downcountAmount += MIPSGetInstructionCycleEstimate(op); +} + void Jit::CompileDelaySlot(int flags) { // preserve flag around the delay slot! Maybe this is not always necessary on ARM where @@ -117,6 +163,17 @@ void Jit::Compile(u32 em_address) int block_num = blocks.AllocateBlock(em_address); ArmJitBlock *b = blocks.GetBlock(block_num); blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, b)); + + // Drat. The VFPU hit an uneaten prefix at the end of a block. + if (js.startDefaultPrefix && js.MayHavePrefix()) + { + js.startDefaultPrefix = false; + // Our assumptions are all wrong so it's clean-slate time. + ClearCache(); + + // Let's try that one more time. We won't get back here because we toggled the value. + Compile(em_address); + } } void Jit::RunLoopUntil(u64 globalticks) @@ -124,6 +181,8 @@ void Jit::RunLoopUntil(u64 globalticks) // TODO: copy globalticks somewhere ((void (*)())enterCode)(); } +static int dontLogBlocks = 20; +int logBlocks = 40; const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { @@ -133,14 +192,14 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) js.curBlock = b; js.compiling = true; js.inDelaySlot = false; + js.PrefixStart(); // We add a check before the block, used when entering from a linked block. b->checkedEntry = GetCodePtr(); // Downcount flag check. The last block decremented downcounter, and the flag should still be available. SetCC(CC_LT); MOVI2R(R0, js.blockStart); - MovToPC(R0); - B((const void *)outerLoop); + B((const void *)outerLoopPCInR0); SetCC(CC_AL); b->normalEntry = GetCodePtr(); @@ -148,11 +207,10 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) MIPSAnalyst::AnalysisResults analysis; // = MIPSAnalyst::Analyze(em_address); gpr.Start(analysis); + fpr.Start(analysis); int numInstructions = 0; int cycles = 0; - static int dontLogBlocks = 20; - static int logBlocks = 40; if (logBlocks > 0) logBlocks--; if (dontLogBlocks > 0) dontLogBlocks--; @@ -163,24 +221,32 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) while (js.compiling) { gpr.SetCompilerPC(js.compilerPC); // Let it know for log messages + fpr.SetCompilerPC(js.compilerPC); u32 inst = Memory::Read_Instruction(js.compilerPC); -#ifdef LOGASM - if (logBlocks > 0 && dontLogBlocks == 0) { - MIPSDisAsm(inst, js.compilerPC, temp, true); - INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); - } -#endif js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst); - // FlushAll(); ///HACKK + js.compilerPC += 4; numInstructions++; + if (!cpu_info.bArmV7 && GetCodePtr() - b->checkedEntry >= 4088) + { + // We need to prematurely flush as we are out of range + CCFlags old_cc = GetCC(); + SetCC(CC_AL); + FixupBranch skip = B(); + FlushLitPool(); + SetJumpTarget(skip); + SetCC(old_cc); + } } + FlushLitPool(); #ifdef LOGASM if (logBlocks > 0 && dontLogBlocks == 0) { - MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); - INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); + for (u32 cpc = em_address; cpc != js.compilerPC + 4; cpc += 4) { + MIPSDisAsm(Memory::Read_Instruction(cpc), cpc, temp, true); + INFO_LOG(DYNA_REC, "M: %08x %s", cpc, temp); + } } #endif @@ -218,6 +284,10 @@ void Jit::Comp_Generic(u32 op) MOVI2R(R0, op); QuickCallFunction(R1, (void *)func); } + + // Might have eaten prefixes, hard to tell... + if ((MIPSGetInfo(op) & IS_VFPU) != 0) + js.PrefixStart(); } void Jit::MovFromPC(ARMReg r) { @@ -266,8 +336,7 @@ void Jit::WriteExit(u32 destination, int exit_num) b->linkStatus[exit_num] = true; } else { MOVI2R(R0, destination); - MovToPC(R0); - B((const void *)dispatcher); + B((const void *)dispatcherPCInR0); } } @@ -298,7 +367,7 @@ void Jit::Comp_DoNothing(u32 op) { } #define _FS ((op>>11) & 0x1F) #define _FT ((op>>16) & 0x1F) #define _FD ((op>>6) & 0x1F) -#define _POS ((op>>6) & 0x1F) +#define _POS ((op>>6) & 0x1F) #define _SIZE ((op>>11) & 0x1F) //memory regions: diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 730a6938ce8f..0d8618c507ef 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -21,6 +21,7 @@ #include "ArmJitCache.h" #include "ArmRegCache.h" +#include "ArmRegCacheFPU.h" #include "ArmAsm.h" namespace MIPSComp @@ -38,6 +39,14 @@ struct ArmJitOptions struct ArmJitState { + enum PrefixState + { + PREFIX_UNKNOWN = 0x00, + PREFIX_KNOWN = 0x01, + PREFIX_DIRTY = 0x10, + PREFIX_KNOWN_DIRTY = 0x11, + }; + u32 compilerPC; u32 blockStart; bool cancel; @@ -45,6 +54,66 @@ struct ArmJitState int downcountAmount; bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block ArmJitBlock *curBlock; + + // VFPU prefix magic + bool startDefaultPrefix; + u32 prefixS; + u32 prefixT; + u32 prefixD; + PrefixState prefixSFlag; + PrefixState prefixTFlag; + PrefixState prefixDFlag; + void PrefixStart() { + if (startDefaultPrefix) { + EatPrefix(); + } else { + PrefixUnknown(); + } + } + void PrefixUnknown() { + prefixSFlag = PREFIX_UNKNOWN; + prefixTFlag = PREFIX_UNKNOWN; + prefixDFlag = PREFIX_UNKNOWN; + } + bool MayHavePrefix() const { + if (HasUnknownPrefix()) { + return true; + } else if (prefixS != 0xE4 || prefixT != 0xE4 || prefixD != 0) { + return true; + } else if (VfpuWriteMask() != 0) { + return true; + } + + return false; + } + bool HasUnknownPrefix() const { + if (!(prefixSFlag & PREFIX_KNOWN) || !(prefixTFlag & PREFIX_KNOWN) || !(prefixDFlag & PREFIX_KNOWN)) { + return true; + } + return false; + } + void EatPrefix() { + if ((prefixSFlag & PREFIX_KNOWN) == 0 || prefixS != 0xE4) { + prefixSFlag = PREFIX_KNOWN_DIRTY; + prefixS = 0xE4; + } + if ((prefixTFlag & PREFIX_KNOWN) == 0 || prefixT != 0xE4) { + prefixTFlag = PREFIX_KNOWN_DIRTY; + prefixT = 0xE4; + } + if ((prefixDFlag & PREFIX_KNOWN) == 0 || prefixD != 0x0 || VfpuWriteMask() != 0) { + prefixDFlag = PREFIX_KNOWN_DIRTY; + prefixD = 0x0; + } + } + u8 VfpuWriteMask() const { + _assert_(prefixDFlag & PREFIX_KNOWN); + return (prefixD >> 8) & 0xF; + } + bool VfpuWriteMask(int i) const { + _assert_(prefixDFlag & PREFIX_KNOWN); + return (prefixD >> (8 + i)) & 1; + } }; @@ -64,6 +133,7 @@ class Jit : public ArmGen::ARMXCodeBlock { public: Jit(MIPSState *mips); + void DoState(PointerWrap &p); // Compiled ops should ignore delay slots // the compiler will take care of them by itself @@ -77,6 +147,7 @@ class Jit : public ArmGen::ARMXCodeBlock void CompileDelaySlot(int flags); void CompileAt(u32 addr); + void EatInstruction(u32 op); void Comp_RunBlock(u32 op); // Ops @@ -93,6 +164,7 @@ class Jit : public ArmGen::ARMXCodeBlock void Comp_Break(u32 op); void Comp_IType(u32 op); + void Comp_RType2(u32 op); void Comp_RType3(u32 op); void Comp_ShiftType(u32 op); void Comp_Allegrex(u32 op); @@ -109,19 +181,26 @@ class Jit : public ArmGen::ARMXCodeBlock void Comp_SV(u32 op); void Comp_SVQ(u32 op); void Comp_VPFX(u32 op); + void Comp_VVectorInit(u32 op); void Comp_VDot(u32 op); void Comp_VecDo3(u32 op); + void Comp_VV2Op(u32 op); void Comp_Mftv(u32 op); void Comp_Vmtvc(u32 op); + void Comp_Vmmov(u32 op); ArmJitBlockCache *GetBlockCache() { return &blocks; } void ClearCache(); void ClearCacheAt(u32 em_address); + // TODO: Eat VFPU prefixes here. + void EatPrefix() { } + private: void GenerateFixedCode(); void FlushAll(); + void FlushPrefixV(); void WriteDownCount(int offset = 0); void MovFromPC(ARMReg r); @@ -140,15 +219,29 @@ class Jit : public ArmGen::ARMXCodeBlock // Utilities to reduce duplicated code void CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)); void CompShiftImm(u32 op, ArmGen::ShiftType shiftType); + void CompShiftVar(u32 op, ArmGen::ShiftType shiftType); void LogBlockNumber(); - /* + + void ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz); + void ApplyPrefixD(const u8 *vregs, VectorSize sz); + void GetVectorRegsPrefixS(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixSFlag & ArmJitState::PREFIX_KNOWN); + GetVectorRegs(regs, sz, vectorReg); + ApplyPrefixST(regs, js.prefixS, sz); + } + void GetVectorRegsPrefixT(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixTFlag & ArmJitState::PREFIX_KNOWN); + GetVectorRegs(regs, sz, vectorReg); + ApplyPrefixST(regs, js.prefixT, sz); + } + void GetVectorRegsPrefixD(u8 *regs, VectorSize sz, int vectorReg); + + /* void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompTriArith(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompShiftImm(u32 op, void (ARMXEmitter::*shift)(int, OpArg, OpArg)); void CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)); - - void CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters); */ // Utils @@ -159,7 +252,7 @@ class Jit : public ArmGen::ARMXCodeBlock ArmJitState js; ArmRegCache gpr; - // FPURegCache fpr; + ArmRegCacheFPU fpr; MIPSState *mips_; @@ -168,7 +261,9 @@ class Jit : public ArmGen::ARMXCodeBlock const u8 *enterCode; const u8 *outerLoop; + const u8 *outerLoopPCInR0; const u8 *dispatcherCheckCoreState; + const u8 *dispatcherPCInR0; const u8 *dispatcher; const u8 *dispatcherNoCheck; diff --git a/Core/MIPS/ARM/ArmJitCache.cpp b/Core/MIPS/ARM/ArmJitCache.cpp index 62ec8fc49692..57c86e9c45a1 100644 --- a/Core/MIPS/ARM/ArmJitCache.cpp +++ b/Core/MIPS/ARM/ArmJitCache.cpp @@ -355,7 +355,7 @@ void ArmJitBlockCache::DestroyBlock(int block_num, bool invalidate) // checkedEntry is the only "linked" entrance so it's enough to overwrite that. ARMXEmitter emit((u8 *)b.checkedEntry); emit.MOVI2R(R0, b.originalAddress); - emit.STR(R10, R0, offsetof(MIPSState, pc)); + emit.STR(CTXREG, R0, offsetof(MIPSState, pc)); emit.B(MIPSComp::jit->dispatcher); emit.FlushIcache(); } diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 6463e1349cd5..5a7f5cac50f4 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -20,8 +20,6 @@ using namespace ArmGen; -#define CTXREG (R10) - ArmRegCache::ArmRegCache(MIPSState *mips) : mips_(mips) { } @@ -30,7 +28,7 @@ void ArmRegCache::Init(ARMXEmitter *emitter) { } void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { - for (int i = 0; i < 16; i++) { + for (int i = 0; i < NUM_ARMREG; i++) { ar[i].mipsReg = -1; ar[i].isDirty = false; } @@ -46,16 +44,18 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) { // Note that R0 is reserved as scratch for now. // R1 could be used as it's only used for scratch outside "regalloc space" now. // R12 is also potentially usable. - // R4-R7 are registers we could use for static allocation. + // R4-R7 are registers we could use for static allocation or downcount. // R8 is used to preserve flags in nasty branches. // R9 and upwards are reserved for jit basics. static const ARMReg allocationOrder[] = { - R2, R3, R4, R5, R6, R7 + R12, R1, R2, R3, R4, R5, R6, R7, }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; } +// TODO: Somewhat smarter spilling - currently simply spills the first available, should do +// round robin or FIFO or something. ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { // Let's see if it's already mapped. If so we just need to update the dirty flag. // We don't need to check for ML_NOINIT because we assume that anyone who maps @@ -67,7 +67,7 @@ ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { if (mapFlags & MAP_DIRTY) { ar[mr[mipsReg].reg].isDirty = true; } - return mr[mipsReg].reg; + return (ARMReg)mr[mipsReg].reg; } // Okay, not mapped, so we need to allocate an ARM register. @@ -135,16 +135,16 @@ void ArmRegCache::MapInIn(MIPSReg rd, MIPSReg rs) { void ArmRegCache::MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad) { SpillLock(rd, rs); - bool overlap = avoidLoad && rd == rs; - MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); + bool load = !avoidLoad || rd == rs; + MapReg(rd, MAP_DIRTY | (load ? 0 : MAP_NOINIT)); MapReg(rs); ReleaseSpillLocks(); } void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad) { SpillLock(rd, rs, rt); - bool overlap = avoidLoad && (rd == rs || rd == rt); - MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); + bool load = !avoidLoad || (rd == rs || rd == rt); + MapReg(rd, MAP_DIRTY | (load ? 0 : MAP_NOINIT)); MapReg(rt); MapReg(rs); ReleaseSpillLocks(); @@ -152,10 +152,10 @@ void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoa void ArmRegCache::MapDirtyDirtyInIn(MIPSReg rd1, MIPSReg rd2, MIPSReg rs, MIPSReg rt, bool avoidLoad) { SpillLock(rd1, rd2, rs, rt); - bool overlap1 = avoidLoad && (rd1 == rs || rd1 == rt); - bool overlap2 = avoidLoad && (rd2 == rs || rd2 == rt); - MapReg(rd1, MAP_DIRTY | (overlap1 ? 0 : MAP_NOINIT)); - MapReg(rd2, MAP_DIRTY | (overlap2 ? 0 : MAP_NOINIT)); + bool load1 = !avoidLoad || (rd1 == rs || rd1 == rt); + bool load2 = !avoidLoad || (rd2 == rs || rd2 == rt); + MapReg(rd1, MAP_DIRTY | (load1 ? 0 : MAP_NOINIT)); + MapReg(rd2, MAP_DIRTY | (load2 ? 0 : MAP_NOINIT)); MapReg(rt); MapReg(rs); ReleaseSpillLocks(); @@ -180,7 +180,7 @@ void ArmRegCache::FlushArmReg(ARMReg r) { ar[r].mipsReg = -1; } -void ArmRegCache::FlushMipsReg(MIPSReg r) { +void ArmRegCache::FlushR(MIPSReg r) { switch (mr[r].loc) { case ML_IMM: // IMM is always "dirty". @@ -189,11 +189,11 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { break; case ML_ARMREG: - if (mr[r].reg == INVALID_REG) { + if (mr[r].reg == (int)INVALID_REG) { ERROR_LOG(HLE, "FlushMipsReg: MipsReg had bad ArmReg"); } if (ar[mr[r].reg].isDirty) { - emit->STR(CTXREG, mr[r].reg, GetMipsRegOffset(r)); + emit->STR(CTXREG, (ARMReg)mr[r].reg, GetMipsRegOffset(r)); ar[mr[r].reg].isDirty = false; } ar[mr[r].reg].mipsReg = -1; @@ -214,7 +214,7 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { void ArmRegCache::FlushAll() { for (int i = 0; i < NUM_MIPSREG; i++) { - FlushMipsReg(i); + FlushR(i); } // Sanity check for (int i = 0; i < NUM_ARMREG; i++) { @@ -225,6 +225,9 @@ void ArmRegCache::FlushAll() { } void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { + if (r == 0) + ERROR_LOG(JIT, "Trying to set immediate %08x to r0", immVal); + // Zap existing value if cached in a reg if (mr[r].loc == ML_ARMREG) { ar[mr[r].reg].mipsReg = -1; @@ -236,10 +239,12 @@ void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { } bool ArmRegCache::IsImm(MIPSReg r) const { + if (r == 0) return true; return mr[r].loc == ML_IMM; } u32 ArmRegCache::GetImm(MIPSReg r) const { + if (r == 0) return 0; if (mr[r].loc != ML_IMM) { ERROR_LOG(JIT, "Trying to get imm from non-imm register %i", r); } @@ -274,7 +279,7 @@ void ArmRegCache::ReleaseSpillLocks() { ARMReg ArmRegCache::R(int mipsReg) { if (mr[mipsReg].loc == ML_ARMREG) { - return mr[mipsReg].reg; + return (ARMReg)mr[mipsReg].reg; } else { ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_); return INVALID_REG; // BAAAD diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index a06d61a32cd5..3cbe27f4daa2 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -23,6 +23,8 @@ using namespace ArmGen; +#define CTXREG (R10) + // R2 to R8: mapped MIPS regs // R9 = code pointers // R10 = MIPS context @@ -53,7 +55,7 @@ struct RegMIPS { RegMIPSLoc loc; // Data (only one of these is used, depending on loc. Could make a union). u32 imm; - ARMReg reg; + ARMReg reg; // reg index bool spillLock; // if true, this register cannot be spilled. // If loc == ML_MEM, it's back in its location in the CPU context struct. }; @@ -91,7 +93,7 @@ class ArmRegCache void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad = true); void MapDirtyDirtyInIn(MIPSReg rd1, MIPSReg rd2, MIPSReg rs, MIPSReg rt, bool avoidLoad = true); void FlushArmReg(ARMReg r); - void FlushMipsReg(MIPSReg r); + void FlushR(MIPSReg r); void FlushAll(); @@ -102,8 +104,9 @@ class ArmRegCache // For better log output only. void SetCompilerPC(u32 compilerPC) { compilerPC_ = compilerPC; } -private: int GetMipsRegOffset(MIPSReg r); + +private: MIPSState *mips_; ARMXEmitter *emit; u32 compilerPC_; diff --git a/Core/MIPS/ARM/ArmRegCacheFPU.cpp b/Core/MIPS/ARM/ArmRegCacheFPU.cpp index 59314f3d5f8c..98a0f5bca861 100644 --- a/Core/MIPS/ARM/ArmRegCacheFPU.cpp +++ b/Core/MIPS/ARM/ArmRegCacheFPU.cpp @@ -18,3 +18,317 @@ #include "Common/ArmEmitter.h" #include "Core/MIPS/ARM/ArmRegCacheFPU.h" + +using namespace ArmGen; + + +ArmRegCacheFPU::ArmRegCacheFPU(MIPSState *mips) : mips_(mips), vr(mr + 32) { +} + +void ArmRegCacheFPU::Init(ARMXEmitter *emitter) { + emit = emitter; +} + +void ArmRegCacheFPU::Start(MIPSAnalyst::AnalysisResults &stats) { + for (int i = 0; i < NUM_ARMFPUREG; i++) { + ar[i].mipsReg = -1; + ar[i].isDirty = false; + } + for (int i = 0; i < NUM_MIPSFPUREG; i++) { + mr[i].loc = ML_MEM; + mr[i].reg = INVALID_REG; + mr[i].spillLock = false; + mr[i].tempLock = false; + } +} + +static const ARMReg *GetMIPSAllocationOrder(int &count) { + // We conservatively reserve both S0 and S1 as scratch for now. + // Will probably really only need one, if that. + static const ARMReg allocationOrder[] = { + S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 + }; + // With NEON, we'll have many more. + static const ARMReg allocationOrderNEON[] = { + S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15, + S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 + }; + bool useNEON = false; // TODO: Use cpu detect + if (useNEON) { + count = sizeof(allocationOrderNEON) / sizeof(const int); + return allocationOrderNEON; + } else { + count = sizeof(allocationOrder) / sizeof(const int); + return allocationOrder; + } +} + +ARMReg ArmRegCacheFPU::MapReg(MIPSReg mipsReg, int mapFlags) { + // Let's see if it's already mapped. If so we just need to update the dirty flag. + // We don't need to check for ML_NOINIT because we assume that anyone who maps + // with that flag immediately writes a "known" value to the register. + if (mr[mipsReg].loc == ML_ARMREG) { + if (ar[mr[mipsReg].reg].mipsReg != mipsReg) { + ERROR_LOG(HLE, "Register mapping out of sync! %i", mipsReg); + } + if (mapFlags & MAP_DIRTY) { + ar[mr[mipsReg].reg].isDirty = true; + } + //INFO_LOG(HLE, "Already mapped %i to %i", mipsReg, mr[mipsReg].reg); + return (ARMReg)(mr[mipsReg].reg + S0); + } + + // Okay, not mapped, so we need to allocate an ARM register. + + int allocCount; + const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount); + +allocate: + for (int i = 0; i < allocCount; i++) { + int reg = allocOrder[i] - S0; + + if (ar[reg].mipsReg == -1) { + // That means it's free. Grab it, and load the value into it (if requested). + ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; + if (!(mapFlags & MAP_NOINIT)) { + if (mr[mipsReg].loc == ML_MEM && mipsReg < TEMP0) { + emit->VLDR((ARMReg)(reg + S0), CTXREG, GetMipsRegOffset(mipsReg)); + } + } + ar[reg].mipsReg = mipsReg; + mr[mipsReg].loc = ML_ARMREG; + mr[mipsReg].reg = reg; + //INFO_LOG(HLE, "Mapped %i to %i", mipsReg, mr[mipsReg].reg); + return (ARMReg)(reg + S0); + } + } + + + // Still nothing. Let's spill a reg and goto 10. + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + int bestToSpill = -1; + for (int i = 0; i < allocCount; i++) { + int reg = allocOrder[i] - S0; + if (ar[reg].mipsReg != -1 && (mr[ar[reg].mipsReg].spillLock || mr[ar[reg].mipsReg].tempLock)) + continue; + bestToSpill = reg; + break; + } + + if (bestToSpill != -1) { + FlushArmReg((ARMReg)(S0 + bestToSpill)); + goto allocate; + } + + // Uh oh, we have all them spilllocked.... + ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc); + return INVALID_REG; +} + +void ArmRegCacheFPU::MapInIn(MIPSReg rd, MIPSReg rs) { + SpillLock(rd, rs); + MapReg(rd); + MapReg(rs); + ReleaseSpillLocks(); +} + +void ArmRegCacheFPU::MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad) { + SpillLock(rd, rs); + bool overlap = avoidLoad && rd == rs; + MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); + MapReg(rs); + ReleaseSpillLocks(); +} + +void ArmRegCacheFPU::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad) { + SpillLock(rd, rs, rt); + bool overlap = avoidLoad && (rd == rs || rd == rt); + MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); + MapReg(rt); + MapReg(rs); + ReleaseSpillLocks(); +} + +void ArmRegCacheFPU::SpillLockV(const u8 *v, VectorSize sz) { + for (int i = 0; i < GetNumVectorElements(sz); i++) { + vr[v[i]].spillLock = true; + } +} + +void ArmRegCacheFPU::SpillLockV(int vec, VectorSize sz) { + u8 v[4]; + GetVectorRegs(v, sz, vec); + SpillLockV(v, sz); +} + +void ArmRegCacheFPU::MapRegV(int vreg, int flags) { + MapReg(vreg + 32, flags); +} + +void ArmRegCacheFPU::MapRegsV(int vec, VectorSize sz, int flags) { + u8 v[4]; + GetVectorRegs(v, sz, vec); + SpillLockV(v, sz); + for (int i = 0; i < GetNumVectorElements(sz); i++) { + MapRegV(v[i], flags); + } +} + +void ArmRegCacheFPU::MapRegsV(const u8 *v, VectorSize sz, int flags) { + SpillLockV(v, sz); + for (int i = 0; i < GetNumVectorElements(sz); i++) { + MapRegV(v[i], flags); + } +} + +void ArmRegCacheFPU::FlushArmReg(ARMReg r) { + int reg = r - S0; + if (ar[reg].mipsReg == -1) { + // Nothing to do, reg not mapped. + return; + } + if (ar[reg].mipsReg != -1) { + if (ar[reg].isDirty && mr[ar[reg].mipsReg].loc == ML_ARMREG) + { + //INFO_LOG(HLE, "Flushing ARM reg %i", reg); + emit->VSTR(r, CTXREG, GetMipsRegOffset(ar[reg].mipsReg)); + } + // IMMs won't be in an ARM reg. + mr[ar[reg].mipsReg].loc = ML_MEM; + mr[ar[reg].mipsReg].reg = INVALID_REG; + } else { + ERROR_LOG(HLE, "Dirty but no mipsreg?"); + } + ar[reg].isDirty = false; + ar[reg].mipsReg = -1; +} + +void ArmRegCacheFPU::FlushR(MIPSReg r) { + switch (mr[r].loc) { + case ML_IMM: + // IMM is always "dirty". + // IMM is not allowed for FP (yet). + ERROR_LOG(HLE, "Imm in FP register?"); + break; + + case ML_ARMREG: + if (mr[r].reg == (int)INVALID_REG) { + ERROR_LOG(HLE, "FlushR: MipsReg had bad ArmReg"); + } + if (ar[mr[r].reg].isDirty) { + //INFO_LOG(HLE, "Flushing dirty reg %i", mr[r].reg); + emit->VSTR((ARMReg)(mr[r].reg + S0), CTXREG, GetMipsRegOffset(r)); + ar[mr[r].reg].isDirty = false; + } + ar[mr[r].reg].mipsReg = -1; + break; + + case ML_MEM: + // Already there, nothing to do. + break; + + default: + //BAD + break; + } + mr[r].loc = ML_MEM; + mr[r].reg = (int)INVALID_REG; +} + +void ArmRegCacheFPU::DiscardR(MIPSReg r) { + switch (mr[r].loc) { + case ML_IMM: + // IMM is always "dirty". + // IMM is not allowed for FP (yet). + ERROR_LOG(HLE, "Imm in FP register?"); + break; + + case ML_ARMREG: + if (mr[r].reg == (int)INVALID_REG) { + ERROR_LOG(HLE, "DiscardR: MipsReg had bad ArmReg"); + } + // Note that we DO NOT write it back here. That's the whole point of Discard. + ar[mr[r].reg].isDirty = false; + ar[mr[r].reg].mipsReg = -1; + break; + + case ML_MEM: + // Already there, nothing to do. + break; + + default: + //BAD + break; + } + mr[r].loc = ML_MEM; + mr[r].reg = (int)INVALID_REG; + mr[r].tempLock = false; + // spill lock? +} + + +bool ArmRegCacheFPU::IsTempX(ARMReg r) const { + return ar[r - S0].mipsReg >= TEMP0; +} + +int ArmRegCacheFPU::GetTempR() { + for (int r = TEMP0; r < TEMP0 + NUM_TEMPS; ++r) { + if (mr[r].loc == ML_MEM && !mr[r].tempLock) { + mr[r].tempLock = true; + return r; + } + } + + _assert_msg_(DYNA_REC, 0, "Regcache ran out of temp regs, might need to DiscardR() some."); + return -1; +} + + +void ArmRegCacheFPU::FlushAll() { + // Discard temps! + for (int i = TEMP0; i < TEMP0 + NUM_TEMPS; i++) { + DiscardR(i); + } + for (int i = 0; i < NUM_MIPSFPUREG; i++) { + FlushR(i); + } + // Sanity check + for (int i = 0; i < NUM_ARMFPUREG; i++) { + if (ar[i].mipsReg != -1) { + ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg); + } + } +} + +int ArmRegCacheFPU::GetMipsRegOffset(MIPSReg r) { + // These are offsets within the MIPSState structure. First there are the GPRS, then FPRS, then the "VFPURs". + if (r < 32 + 128 + NUM_TEMPS) + return (r + 32) << 2; + ERROR_LOG(JIT, "bad mips register %i", r); + return 0; // or what? +} + +void ArmRegCacheFPU::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3, MIPSReg r4) { + mr[r1].spillLock = true; + if (r2 != -1) mr[r2].spillLock = true; + if (r3 != -1) mr[r3].spillLock = true; + if (r4 != -1) mr[r4].spillLock = true; +} + +// This is actually pretty slow with all the 160 regs... +void ArmRegCacheFPU::ReleaseSpillLocks() { + for (int i = 0; i < NUM_MIPSFPUREG; i++) + mr[i].spillLock = false; + for (int i = TEMP0; i < TEMP0 + NUM_TEMPS; ++i) + DiscardR(i); +} + +ARMReg ArmRegCacheFPU::R(int mipsReg) { + if (mr[mipsReg].loc == ML_ARMREG) { + return (ARMReg)(mr[mipsReg].reg + S0); + } else { + ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_); + return INVALID_REG; // BAAAD + } +} diff --git a/Core/MIPS/ARM/ArmRegCacheFPU.h b/Core/MIPS/ARM/ArmRegCacheFPU.h index 6b9fef234559..c22cbecf9e98 100644 --- a/Core/MIPS/ARM/ArmRegCacheFPU.h +++ b/Core/MIPS/ARM/ArmRegCacheFPU.h @@ -17,13 +17,119 @@ #pragma once +#pragma once + +#include "../MIPS.h" +#include "../MIPSAnalyst.h" #include "Common/ArmEmitter.h" +#include "Core/MIPS/ARM/ArmRegCache.h" +#include "Core/MIPS/MIPSVFPUUtils.h" + +using namespace ArmGen; +enum { + NUM_TEMPS = 16, + TEMP0 = 32 + 128, + TOTAL_MAPPABLE_MIPSFPUREGS = 32 + 128 + NUM_TEMPS, +}; -// The PSP has 160 FP registers: 32 FPRs + 128 VFPU registers. +struct FPURegARM { + int mipsReg; // if -1, no mipsreg attached. + bool isDirty; // Should the register be written back? +}; +struct FPURegMIPS { + // Where is this MIPS register? + RegMIPSLoc loc; + // Data (only one of these is used, depending on loc. Could make a union). + int reg; + bool spillLock; // if true, this register cannot be spilled. + bool tempLock; + // If loc == ML_MEM, it's back in its location in the CPU context struct. +}; -class FPURegCache { + +class ArmRegCacheFPU +{ public: + ArmRegCacheFPU(MIPSState *mips); + ~ArmRegCacheFPU() {} + + void Init(ARMXEmitter *emitter); + void Start(MIPSAnalyst::AnalysisResults &stats); + + // Protect the arm register containing a MIPS register from spilling, to ensure that + // it's being kept allocated. + void SpillLock(MIPSReg reg, MIPSReg reg2 = -1, MIPSReg reg3 = -1, MIPSReg reg4 = -1); + void SpillLockV(MIPSReg r) { SpillLock(r + 32); } + + void ReleaseSpillLocks(); + void ReleaseSpillLock(int mipsreg) + { + mr[mipsreg].spillLock = false; + } + void ReleaseSpillLockV(int mipsreg) { + ReleaseSpillLock(mipsreg + 32); + } + + void SetImm(MIPSReg reg, u32 immVal); + bool IsImm(MIPSReg reg) const; + u32 GetImm(MIPSReg reg) const; + + // Returns an ARM register containing the requested MIPS register. + ARMReg MapReg(MIPSReg reg, int mapFlags = 0); + void MapInIn(MIPSReg rd, MIPSReg rs); + void MapDirty(MIPSReg rd); + void MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad = true); + void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad = true); + void FlushArmReg(ARMReg r); + void FlushR(MIPSReg r); + void FlushV(MIPSReg r) { FlushR(r + 32); } + void DiscardR(MIPSReg r); + void DiscardV(MIPSReg r) { DiscardR(r + 32);} + bool IsTempX(ARMReg r) const; + + MIPSReg GetTempR(); + MIPSReg GetTempV() { return GetTempR() - 32; } + + void FlushAll(); + + ARMReg R(int preg); // Returns a cached register + + // VFPU registers + + ARMReg V(int vreg) { return R(vreg + 32); } + + void MapRegV(int vreg, int flags = 0); + + // NOTE: These require you to release spill locks manually! + void MapRegsV(int vec, VectorSize vsz, int flags); + void MapRegsV(const u8 *v, VectorSize vsz, int flags); + + void SpillLockV(const u8 *v, VectorSize vsz); + void SpillLockV(int vec, VectorSize vsz); + + void SetEmitter(ARMXEmitter *emitter) { emit = emitter; } + + // For better log output only. + void SetCompilerPC(u32 compilerPC) { compilerPC_ = compilerPC; } + + int GetMipsRegOffset(MIPSReg r); + int GetMipsRegOffsetV(MIPSReg r) { + return GetMipsRegOffset(r + 32); + } + +private: + MIPSState *mips_; + ARMXEmitter *emit; + u32 compilerPC_; + + enum { + NUM_ARMFPUREG = 16, // TODO: Support 32, which you have with NEON + NUM_MIPSFPUREG = TOTAL_MAPPABLE_MIPSFPUREGS, + }; -}; \ No newline at end of file + RegARM ar[NUM_ARMFPUREG]; + FPURegMIPS mr[NUM_MIPSFPUREG]; + FPURegMIPS *vr; +}; diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 478d2c99e509..f7fc64a752b5 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -45,8 +45,11 @@ MIPSState::MIPSState() MIPSState::~MIPSState() { - delete MIPSComp::jit; - MIPSComp::jit = 0; + if (MIPSComp::jit) + { + delete MIPSComp::jit; + MIPSComp::jit = 0; + } } void MIPSState::Reset() @@ -107,6 +110,8 @@ void MIPSState::DoState(PointerWrap &p) // Reset the jit if we're loading. if (p.mode == p.MODE_READ) Reset(); + if (MIPSComp::jit) + MIPSComp::jit->DoState(p); p.DoArray(r, sizeof(r) / sizeof(r[0])); p.DoArray(f, sizeof(f) / sizeof(f[0])); diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 82e771927b00..c9c548a73983 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -98,7 +98,7 @@ namespace MIPSAnalyst "slt", "sltu", }; const char *opName = MIPSGetName(op); - for (int i = 0; i < ARRAY_SIZE(safeOps); ++i) + for (size_t i = 0; i < ARRAY_SIZE(safeOps); ++i) { if (!strcmp(safeOps[i], opName)) return true; diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 46ae3ae43955..70e70a900f98 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -74,7 +74,40 @@ class MIPSDebugInterface : public DebugInterface u32 GetRegValue(int cat, int index) { - return cpu->r[index]; + switch (cat) + { + case 0: + return cpu->r[index]; + + case 1: + return *(u32 *)&cpu->f[index]; + + case 2: + return *(u32 *)&cpu->v[index]; + + default: + return 0; + } } + void SetRegValue(int cat, int index, u32 value) + { + switch (cat) + { + case 0: + cpu->r[index] = value; + break; + + case 1: + cpu->f[index] = *(float *)&value; + break; + + case 2: + cpu->v[index] = *(float *)&value; + break; + + default: + break; + } + } }; diff --git a/Core/MIPS/MIPSDis.cpp b/Core/MIPS/MIPSDis.cpp index 3a72a794188b..638f84ecdfa6 100644 --- a/Core/MIPS/MIPSDis.cpp +++ b/Core/MIPS/MIPSDis.cpp @@ -76,7 +76,7 @@ namespace MIPSDis int ft = _FT; int rs = _RS; const char *name = MIPSGetName(op); - sprintf(out, "%s\t%s, %d(%s)",name,FN(ft),offset,RN(rs)); + sprintf(out, "%s\t%s, 0x%X(%s)",name,FN(ft),offset,RN(rs)); } void Dis_FPUComp(u32 op, char *out) { @@ -151,7 +151,7 @@ namespace MIPSDis int rt = _RT; int rs = _RS; const char *name = MIPSGetName(op); - sprintf(out, "%s\t%s, %s, %i",name,RN(rt),RN(rs),imm); + sprintf(out, "%s\t%s, %s, 0x%X",name,RN(rt),RN(rs),imm); } void Dis_ori(u32 op, char *out) { @@ -160,9 +160,9 @@ namespace MIPSDis int rs = _RS; const char *name = MIPSGetName(op); if (rs == 0) - sprintf(out, "li\t%s, %i",RN(rt),imm); + sprintf(out, "li\t%s, 0x%X",RN(rt),imm); else - sprintf(out, "%s\t%s, %s, %i",name,RN(rt),RN(rs),imm); + sprintf(out, "%s\t%s, %s, 0x%X",name,RN(rt),RN(rs),imm); } void Dis_IType1(u32 op, char *out) @@ -170,7 +170,7 @@ namespace MIPSDis int imm = (signed short)(op&0xFFFF); int rt = _RT; const char *name = MIPSGetName(op); - sprintf(out, "%s\t%s, %i",name,RN(rt),imm); + sprintf(out, "%s\t%s, 0x%X",name,RN(rt),imm); } void Dis_addi(u32 op, char *out) @@ -179,7 +179,7 @@ namespace MIPSDis int rt = _RT; int rs = _RS; if (rs == 0) - sprintf(out, "li\t%s, %i",RN(rt),imm); + sprintf(out, "li\t%s, 0x%X",RN(rt),imm); else Dis_IType(op,out); } @@ -190,7 +190,7 @@ namespace MIPSDis int rt = _RT; int rs = _RS; const char *name = MIPSGetName(op); - sprintf(out, "%s\t%s, %i(%s)",name,RN(rt),imm,RN(rs)); + sprintf(out, "%s\t%s, 0x%X(%s)",name,RN(rt),imm,RN(rs)); } void Dis_RType2(u32 op, char *out) @@ -237,7 +237,7 @@ namespace MIPSDis name = "rotr"; if (((op & 0x3f) == 6) && sa == 1) name = "rotrv"; - sprintf(out, "%s\t%s, %s, %d",name,RN(rd),RN(rt),sa); + sprintf(out, "%s\t%s, %s, 0x%X",name,RN(rd),RN(rt),sa); } void Dis_VarShiftType(u32 op, char *out) @@ -271,13 +271,13 @@ namespace MIPSDis case 0x0: //ext { int size = _SIZE + 1; - sprintf(out,"%s\t%s, %s, %i, %i",name,RN(Rt),RN(rs),pos,size); + sprintf(out,"%s\t%s, %s, 0x%X, 0x%X",name,RN(Rt),RN(rs),pos,size); } break; case 0x4: // ins { int size = (_SIZE + 1) - pos; - sprintf(out,"%s\t%s, %s, %i, %i",name,RN(Rt),RN(rs),pos,size); + sprintf(out,"%s\t%s, %s, 0x%X, 0x%X",name,RN(Rt),RN(rs),pos,size); } break; } diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 9fb5d01308be..8d423db81f3a 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -23,6 +23,7 @@ #include "MIPS.h" #include "MIPSInt.h" #include "MIPSTables.h" +#include "Core/Reporting.h" #include "../HLE/HLE.h" #include "../System.h" @@ -149,6 +150,10 @@ namespace MIPSInt // It appears that a cache line is 0x40 (64) bytes. switch (func) { + case 24: + // "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it. + // Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline. + break; case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary. // No need to do anything. break; @@ -186,8 +191,9 @@ namespace MIPSInt void Int_Break(u32 op) { + Reporting::ReportMessage("BREAK instruction hit"); ERROR_LOG(CPU, "BREAK!"); - coreState = CORE_STEPPING; + Core_UpdateState(CORE_STEPPING); PC += 4; } @@ -304,7 +310,6 @@ namespace MIPSInt _dbg_assert_msg_(CPU,0,"Jump in delay slot :("); } - int rs = (op>>21)&0x1f; u32 addr = R(rs); switch (op & 0x3f) @@ -362,14 +367,18 @@ namespace MIPSInt switch (op >> 26) { case 48: // ll - R(rt) = Memory::Read_U32(addr); + if (rt != 0) { + R(rt) = Memory::Read_U32(addr); + } currentMIPS->llBit = 1; break; case 56: // sc if (currentMIPS->llBit) { Memory::Write_U32(R(rt), addr); - R(rt) = 1; - } else { + if (rt != 0) { + R(rt) = 1; + } + } else if (rt != 0) { R(rt) = 0; } break; @@ -388,6 +397,13 @@ namespace MIPSInt int rd = _RD; static bool has_warned = false; + // Don't change $zr. + if (rd == 0) + { + PC += 4; + return; + } + switch (op & 63) { case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz @@ -495,7 +511,7 @@ namespace MIPSInt void Int_FPULS(u32 op) { s32 offset = (s16)(op&0xFFFF); - int ft = ((op>>16)&0x1f); + int ft = _FT; int rs = _RS; u32 addr = R(rs) + offset; @@ -533,6 +549,14 @@ namespace MIPSInt { int rs = _RS; int rd = _RD; + + // Don't change $zr. + if (rd == 0) + { + PC += 4; + return; + } + switch (op & 63) { case 22: //clz @@ -675,6 +699,13 @@ namespace MIPSInt int rs = _RS; int rd = _RD; int sa = _FD; + + // Don't change $zr. + if (rd == 0) + { + PC += 4; + return; + } switch (op & 0x3f) { @@ -720,6 +751,14 @@ namespace MIPSInt { int rt = _RT; int rd = _RD; + + // Don't change $zr. + if (rd == 0) + { + PC += 4; + return; + } + switch((op>>6)&31) { case 16: // seb @@ -756,6 +795,13 @@ namespace MIPSInt int rt = _RT; int rd = _RD; + // Don't change $zr. + if (rd == 0) + { + PC += 4; + return; + } + switch (op & 0x3ff) { case 0xA0: //wsbh @@ -777,6 +823,13 @@ namespace MIPSInt int rt = _RT; int pos = _POS; + // Don't change $zr. + if (rt == 0) + { + PC += 4; + return; + } + switch (op & 0x3f) { case 0x0: //ext @@ -785,7 +838,7 @@ namespace MIPSInt R(rt) = (R(rs) >> pos) & ((1< @@ -84,7 +85,34 @@ #ifdef __APPLE__ using std::isnan; +using std::isinf; #endif +#ifdef _MSC_VER +#define isnan _isnan +#define isinf(x) (!_finite(x) && !_isnan(x)) +#endif +#ifdef ANDROID +using __captured::isinf; +#endif + +// Preserves NaN in first param, takes sign of equal second param. +// Technically, std::max may do this but it's undefined. +inline float nanmax(float f, float cst) +{ + return f <= cst ? cst : f; +} + +// Preserves NaN in first param, takes sign of equal second param. +inline float nanmin(float f, float cst) +{ + return f >= cst ? cst : f; +} + +// Preserves NaN in first param, takes sign of equal value in others. +inline float nanclamp(float f, float lower, float upper) +{ + return nanmin(nanmax(f, lower), upper); +} void ApplyPrefixST(float *v, u32 data, VectorSize size) { @@ -115,6 +143,7 @@ void ApplyPrefixST(float *v, u32 data, VectorSize size) if (regnum >= n) { ERROR_LOG(CPU, "Invalid VFPU swizzle: %08x / %d", data, size); + Reporting::ReportMessage("Invalid VFPU swizzle: %08x / %d", data, size); regnum = 0; } @@ -145,27 +174,22 @@ inline void ApplySwizzleT(float *v, VectorSize size) void ApplyPrefixD(float *v, VectorSize size, bool onlyWriteMask = false) { u32 data = currentMIPS->vfpuCtrl[VFPU_CTRL_DPREFIX]; - if (!data) + if (!data || onlyWriteMask) return; int n = GetNumVectorElements(size); - bool writeMask[4]; for (int i = 0; i < n; i++) { - int mask = (data >> (8 + i)) & 1; - writeMask[i] = mask ? true : false; - if (!onlyWriteMask) { - int sat = (data >> (i * 2)) & 3; - if (sat == 1) - { - if (v[i] > 1.0f) v[i] = 1.0f; - // This includes -0.0f -> +0.0f. - if (v[i] <= 0.0f) v[i] = 0.0f; - } - else if (sat == 3) - { - if (v[i] > 1.0f) v[i] = 1.0f; - if (v[i] < -1.0f) v[i] = -1.0f; - } + int sat = (data >> (i * 2)) & 3; + if (sat == 1) + { + if (v[i] > 1.0f) v[i] = 1.0f; + // This includes -0.0f -> +0.0f. + if (v[i] <= 0.0f) v[i] = 0.0f; + } + else if (sat == 3) + { + if (v[i] > 1.0f) v[i] = 1.0f; + if (v[i] < -1.0f) v[i] = -1.0f; } } } @@ -491,15 +515,16 @@ namespace MIPSInt case 0: d[i] = s[i]; break; //vmov case 1: d[i] = fabsf(s[i]); break; //vabs case 2: d[i] = -s[i]; break; //vneg - case 4: if (s[i] < 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 + // vsat0 changes -0.0 to +0.0. + case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 case 16: d[i] = 1.0f / s[i]; break; //vrcp case 17: d[i] = 1.0f / sqrtf(s[i]); break; //vrsq case 18: d[i] = sinf((float)M_PI_2 * s[i]); break; //vsin case 19: d[i] = cosf((float)M_PI_2 * s[i]); break; //vcos - case 20: d[i] = powf(2.0f, s[i]); break; - case 21: d[i] = logf(s[i])/log(2.0f); break; - case 22: d[i] = sqrtf(s[i]); break; //vsqrt + case 20: d[i] = powf(2.0f, s[i]); break; //vexp2 + case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2 + case 22: d[i] = fabsf(sqrtf(s[i])); break; //vsqrt case 23: d[i] = asinf(s[i] * (float)M_2_PI); break; //vasin case 24: d[i] = -1.0f / s[i]; break; // vnrcp case 26: d[i] = -sinf((float)M_PI_2 * s[i]); break; // vnsin @@ -524,7 +549,8 @@ namespace MIPSInt ReadVector(s, sz, vs); for (int i = 0; i < GetNumVectorElements(sz); i++) { - d[i] = 1.0f - s[i]; + // Always positive NaN. + d[i] = isnan(s[i]) ? fabsf(s[i]) : 1.0f - s[i]; } ApplyPrefixD(d, sz); WriteVector(d, sz, vd); @@ -541,13 +567,13 @@ namespace MIPSInt ReadVector(s, sz, vs); int n = GetNumVectorElements(sz); float x = s[0]; - d[0] = std::min(std::max(0.0f, 1.0f - x), 1.0f); - d[1] = std::min(std::max(0.0f, x), 1.0f); + d[0] = nanclamp(1.0f - x, 0.0f, 1.0f); + d[1] = nanclamp(x, 0.0f, 1.0f); VectorSize outSize = V_Pair; if (n > 1) { float y = s[1]; - d[2] = std::min(std::max(0.0f, 1.0f - y), 1.0f); - d[3] = std::min(std::max(0.0f, y), 1.0f); + d[2] = nanclamp(1.0f - y, 0.0f, 1.0f); + d[3] = nanclamp(y, 0.0f, 1.0f); outSize = V_Quad; } WriteVector(d, outSize, vd); @@ -621,8 +647,8 @@ namespace MIPSInt int imm = (op >> 16) & 0x1f; float mult = 1.0f/(float)(1 << imm); VectorSize sz = GetVecSize(op); - ReadVector((float*)&s, sz, vs); - ApplySwizzleS((float*)&s, sz); //TODO: and the mask to kill everything but swizzle + ReadVector((float*)&s[0], sz, vs); + ApplySwizzleS((float*)&s[0], sz); //TODO: and the mask to kill everything but swizzle for (int i = 0; i < GetNumVectorElements(sz); i++) { d[i] = (float)s[i] * mult; @@ -643,6 +669,7 @@ namespace MIPSInt }; // magic code from ryg: http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ + // See also SSE2 version: https://gist.github.com/rygorous/2144712 static FP32 half_to_float_fast5(FP16 h) { static const FP32 magic = { (127 + (127 - 15)) << 23 }; @@ -663,6 +690,42 @@ namespace MIPSInt return fp.f; } + // More magic code: https://gist.github.com/rygorous/2156668 + static FP16 float_to_half_fast3(FP32 f) + { + static const FP32 f32infty = { 255 << 23 }; + static const FP32 f16infty = { 31 << 23 }; + static const FP32 magic = { 15 << 23 }; + static const u32 sign_mask = 0x80000000u; + static const u32 round_mask = ~0xfffu; + FP16 o = { 0 }; + + u32 sign = f.u & sign_mask; + f.u ^= sign; + + if (f.u >= f32infty.u) // Inf or NaN (all exponent bits set) + o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + else // (De)normalized number or zero + { + f.u &= round_mask; + f.f *= magic.f; + f.u -= round_mask; + if (f.u > f16infty.u) f.u = f16infty.u; // Clamp to signed infinity if overflowed + + o.u = f.u >> 13; // Take the bits! + } + + o.u |= sign >> 16; + return o; + } + + static u16 ShrinkToHalf(float full) { + FP32 fp32; + fp32.f = full; + FP16 fp = float_to_half_fast3(fp32); + return fp.u; + } + void Int_Vh2f(u32 op) { u32 s[4]; @@ -670,7 +733,8 @@ namespace MIPSInt int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); - ReadVector((float*)&s, sz, vs); + ReadVector((float*)&s[0], sz, vs); + ApplySwizzleS((float*)&s[0], sz); VectorSize outsize = V_Pair; switch (sz) { @@ -691,35 +755,40 @@ namespace MIPSInt _dbg_assert_msg_(CPU, 0, "Trying to interpret Int_Vh2f instruction that can't be interpreted"); break; } - ApplyPrefixD(d, sz); //TODO: and the mask to kill everything but mask - WriteVector(d, sz, vd); + ApplyPrefixD(d, outsize); + WriteVector(d, outsize, vd); PC += 4; EatPrefixes(); } void Int_Vf2h(u32 op) { - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); - // See http://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion - - /* - int s[4]; - float d[4]; + float s[4]; + u32 d[4]; int vd = _VD; int vs = _VS; - int imm = (op >> 16) & 0x1f; - float mult = 1.0f/(float)(1 << imm); VectorSize sz = GetVecSize(op); - ReadVector((float*)&s, sz, vs); - ApplySwizzleS((float*)&s, sz); //TODO: and the mask to kill everything but swizzle + ReadVector(s, sz, vs); + ApplySwizzleS(s, sz); - for (int i = 0; i < GetNumVectorElements(sz); i++) - { - d[i] = (float)s[i] * mult; + VectorSize outsize = V_Single; + switch (sz) { + case V_Pair: + outsize = V_Single; + d[0] = ShrinkToHalf(s[0]) | ((u32)ShrinkToHalf(s[1]) << 16); + break; + case V_Quad: + outsize = V_Pair; + d[0] = ShrinkToHalf(s[0]) | ((u32)ShrinkToHalf(s[1]) << 16); + d[1] = ShrinkToHalf(s[2]) | ((u32)ShrinkToHalf(s[3]) << 16); + break; + case V_Single: + case V_Triple: + _dbg_assert_msg_(CPU, 0, "Trying to interpret Int_Vf2h instruction that can't be interpreted"); + break; } - ApplyPrefixD(d, sz); //TODO: and the mask to kill everything but mask - WriteVector(d, sz, vd); - */ + ApplyPrefixD((float*)&d[0], outsize); + WriteVector((float*)&d[0], outsize, vd); PC += 4; EatPrefixes(); } @@ -727,7 +796,7 @@ namespace MIPSInt void Int_Vx2i(u32 op) { int s[4]; - u32 d[4] = {0}; + u32 d[4] = {0}; int vd = _VD; int vs = _VS; VectorSize sz = GetVecSize(op); @@ -859,9 +928,10 @@ namespace MIPSInt break; default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + oz = V_Single; break; } - ApplyPrefixD((float*)d,oz,true); + ApplyPrefixD((float*)d,oz); WriteVector((float*)d,oz,vd); PC += 4; EatPrefixes(); @@ -936,8 +1006,7 @@ namespace MIPSInt } d = sum; ApplyPrefixD(&d,V_Single); - // TODO: Shouldn't this respect the mask? - V(vd) = d; + WriteVector(&d, V_Single, vd); PC += 4; EatPrefixes(); } @@ -960,7 +1029,7 @@ namespace MIPSInt { sum += (i == n - 1) ? t[i] : s[i]*t[i]; } - d = sum; + d = isnan(sum) ? fabsf(sum) : sum; ApplyPrefixD(&d,V_Single); V(vd) = d; PC += 4; @@ -1191,7 +1260,7 @@ namespace MIPSInt float scale = V(vt); if (currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX] != 0xE4) { - WARN_LOG(CPU, "Broken T prefix used with VScl: %08x / %08x", currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX], op); + // WARN_LOG(CPU, "Broken T prefix used with VScl: %08x / %08x", currentMIPS->vfpuCtrl[VFPU_CTRL_TPREFIX], op); ApplySwizzleT(&scale, V_Single); } int n = GetNumVectorElements(sz); @@ -1211,6 +1280,7 @@ namespace MIPSInt int seed = VI(vd); currentMIPS->rng.Init(seed); PC += 4; + EatPrefixes(); } void Int_VrndX(u32 op) @@ -1457,10 +1527,6 @@ namespace MIPSInt VC_NS }; -#ifdef _MSC_VER -#define isnan _isnan -#endif - void Int_Vcmp(u32 op) { int vs = _VS; @@ -1496,16 +1562,18 @@ namespace MIPSInt case VC_EZ: c = s[i] == 0.0f || s[i] == -0.0f; break; case VC_EN: c = isnan(s[i]); break; - case VC_EI: c = 0; break; - case VC_ES: c = (s[i] != s[i]) || (s[i] == std::numeric_limits::infinity()); break; // Tekken Dark Resurrection + case VC_EI: c = isinf(s[i]); break; + case VC_ES: c = isnan(s[i]) || isinf(s[i]); break; // Tekken Dark Resurrection case VC_NZ: c = s[i] != 0; break; - case VC_NN: c = s[i] != 0; break; - case VC_NI: c = s[i] != 0; break; - case VC_NS: c = s[i] != 0; break; + case VC_NN: c = !isnan(s[i]); break; + case VC_NI: c = !isinf(s[i]); break; + case VC_NS: c = !isnan(s[i]) && !isinf(s[i]); break; + default: _dbg_assert_msg_(CPU,0,"Unsupported vcmp condition code %d", cond); PC += 4; + EatPrefixes(); return; } cc |= (c<> 23) & 3) { case 2: // vmin - for (int i = 0; i < GetNumVectorElements(sz); i++) + for (int i = 0; i < numElements; i++) d[i] = isnan(t[i]) ? s[i] : (isnan(s[i]) ? t[i] : std::min(s[i], t[i])); break; case 3: // vmax - for (int i = 0; i < GetNumVectorElements(sz); i++) + for (int i = 0; i < numElements; i++) d[i] = isnan(t[i]) ? t[i] : (isnan(s[i]) ? s[i] : std::max(s[i], t[i])); break; default: @@ -1756,22 +1824,26 @@ namespace MIPSInt void Int_Vlgb(u32 op) { // S & D valid + Reporting::ReportMessage("vlgb not implemented"); _dbg_assert_msg_(CPU,0,"vlgb not implemented"); } void Int_Vwbn(u32 op) { // S & D valid + Reporting::ReportMessage("vwbn not implemented"); _dbg_assert_msg_(CPU,0,"vwbn not implemented"); } void Int_Vsbn(u32 op) { + Reporting::ReportMessage("vsbn not implemented"); _dbg_assert_msg_(CPU,0,"vsbn not implemented"); } void Int_Vsbz(u32 op) { + Reporting::ReportMessage("vsbz not implemented"); _dbg_assert_msg_(CPU,0,"vsbz not implemented"); } } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 06e30466c30c..a27774bd3d31 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -147,22 +147,22 @@ const MIPSInstruction tableImmediate[64] = //xxxxxx ..... //48 INSTR("ll", &Jit::Comp_Generic, Dis_Generic, Int_StoreSync, 0), INSTR("lwc1", &Jit::Comp_FPULS, Dis_FPULS, Int_FPULS, IN_RT|IN_RS_ADDR), - INSTR("lv.s", &Jit::Comp_SV, Dis_SV, Int_SV, IS_VFPU), + INSTR("lv.s", &Jit::Comp_SV, Dis_SV, Int_SV, IS_VFPU|VFPU_NO_PREFIX), {-2}, // HIT THIS IN WIPEOUT {VFPU4Jump}, - INSTR("lv", &Jit::Comp_SVQ, Dis_SVLRQ, Int_SVQ, IS_VFPU), - INSTR("lv.q", &Jit::Comp_SVQ, Dis_SVQ, Int_SVQ, IS_VFPU), //copU + INSTR("lv", &Jit::Comp_SVQ, Dis_SVLRQ, Int_SVQ, IS_VFPU|VFPU_NO_PREFIX), + INSTR("lv.q", &Jit::Comp_SVQ, Dis_SVQ, Int_SVQ, IS_VFPU|VFPU_NO_PREFIX), //copU {VFPU5}, //56 INSTR("sc", &Jit::Comp_Generic, Dis_Generic, Int_StoreSync, 0), INSTR("swc1", &Jit::Comp_FPULS, Dis_FPULS, Int_FPULS, 0), //copU - INSTR("sv.s", &Jit::Comp_SV, Dis_SV, Int_SV,IS_VFPU), + INSTR("sv.s", &Jit::Comp_SV, Dis_SV, Int_SV,IS_VFPU|VFPU_NO_PREFIX), {-2}, //60 {VFPU6}, - INSTR("sv", &Jit::Comp_SVQ, Dis_SVLRQ, Int_SVQ, IS_VFPU), //copU - INSTR("sv.q", &Jit::Comp_SVQ, Dis_SVQ, Int_SVQ, IS_VFPU), - INSTR("vflush", &Jit::Comp_Generic, Dis_Vflush, Int_Vflush, IS_VFPU), + INSTR("sv", &Jit::Comp_SVQ, Dis_SVLRQ, Int_SVQ, IS_VFPU|VFPU_NO_PREFIX), //copU + INSTR("sv.q", &Jit::Comp_SVQ, Dis_SVQ, Int_SVQ, IS_VFPU|VFPU_NO_PREFIX), + INSTR("vflush", &Jit::Comp_Generic, Dis_Vflush, Int_Vflush, IS_VFPU|VFPU_NO_PREFIX), }; const MIPSInstruction tableSpecial[64] = /// 000000 ...... ...... .......... xxxxxx @@ -194,8 +194,8 @@ const MIPSInstruction tableSpecial[64] = /// 000000 ...... ...... .......... xxx INSTR("mtlo", &Jit::Comp_MulDivType, Dis_ToHiloTransfer, Int_MulDivType, IN_RS|OUT_OTHER), {-2}, {-2}, - INSTR("clz", &Jit::Comp_Generic, Dis_RType2, Int_RType2, OUT_RD|IN_RS|IN_RT), - INSTR("clo", &Jit::Comp_Generic, Dis_RType2, Int_RType2, OUT_RD|IN_RS|IN_RT), + INSTR("clz", &Jit::Comp_RType2, Dis_RType2, Int_RType2, OUT_RD|IN_RS|IN_RT), + INSTR("clo", &Jit::Comp_RType2, Dis_RType2, Int_RType2, OUT_RD|IN_RS|IN_RT), //24 INSTR("mult", &Jit::Comp_MulDivType, Dis_MulDivType, Int_MulDivType, IN_RS|IN_RT|OUT_OTHER), @@ -478,21 +478,21 @@ const MIPSInstruction tableCop1BC[32] = const MIPSInstruction tableVFPU0[8] = { - INSTR("vadd",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU), - INSTR("vsub",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU), + INSTR("vadd",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsub",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU|OUT_EAT_PREFIX), INSTR("vsbn",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vsbn, IS_VFPU), {-2}, {-2}, {-2}, {-2}, - INSTR("vdiv",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU), + INSTR("vdiv",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU|OUT_EAT_PREFIX), }; const MIPSInstruction tableVFPU1[8] = { - INSTR("vmul",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU), - INSTR("vdot",&Jit::Comp_VDot, Dis_VectorDot, Int_VDot, IS_VFPU), - INSTR("vscl",&Jit::Comp_Generic, Dis_VScl, Int_VScl, IS_VFPU), + INSTR("vmul",&Jit::Comp_VecDo3, Dis_VectorSet3, Int_VecDo3, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vdot",&Jit::Comp_VDot, Dis_VectorDot, Int_VDot, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vscl",&Jit::Comp_Generic, Dis_VScl, Int_VScl, IS_VFPU|OUT_EAT_PREFIX), {-2}, - INSTR("vhdp",&Jit::Comp_Generic, Dis_Generic, Int_VHdp, IS_VFPU), + INSTR("vhdp",&Jit::Comp_Generic, Dis_Generic, Int_VHdp, IS_VFPU|OUT_EAT_PREFIX), INSTR("vcrs",&Jit::Comp_Generic, Dis_Vcrs, Int_Vcrs, IS_VFPU), INSTR("vdet",&Jit::Comp_Generic, Dis_Generic, Int_Vdet, IS_VFPU), {-2}, @@ -500,14 +500,14 @@ const MIPSInstruction tableVFPU1[8] = const MIPSInstruction tableVFPU3[8] = //011011 xxx { - INSTR("vcmp",&Jit::Comp_Generic, Dis_Vcmp, Int_Vcmp, IS_VFPU), + INSTR("vcmp",&Jit::Comp_Generic, Dis_Vcmp, Int_Vcmp, IS_VFPU|OUT_EAT_PREFIX), {-2}, - INSTR("vmin",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU), - INSTR("vmax",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU), + INSTR("vmin",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmax",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vminmax, IS_VFPU|OUT_EAT_PREFIX), {-2}, - INSTR("vscmp",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vscmp, IS_VFPU), - INSTR("vsge",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vsge, IS_VFPU), - INSTR("vslt",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vslt, IS_VFPU), + INSTR("vscmp",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vscmp, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsge",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vsge, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vslt",&Jit::Comp_Generic, Dis_VectorSet3, Int_Vslt, IS_VFPU|OUT_EAT_PREFIX), }; @@ -516,7 +516,7 @@ const MIPSInstruction tableVFPU4Jump[32] = //110100 xxxxx {VFPU4}, {VFPU7}, {VFPU9}, - INSTR("vcst", &Jit::Comp_Generic, Dis_Vcst, Int_Vcst, IS_VFPU), + INSTR("vcst", &Jit::Comp_Generic, Dis_Vcst, Int_Vcst, IS_VFPU|OUT_EAT_PREFIX), {-2},{-2},{-2},{-2}, //8 @@ -524,13 +524,13 @@ const MIPSInstruction tableVFPU4Jump[32] = //110100 xxxxx {-2},{-2},{-2},{-2}, //16 - INSTR("vf2in", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU), - INSTR("vf2iz", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU), - INSTR("vf2iu", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU), - INSTR("vf2id", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU), + INSTR("vf2in", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vf2iz", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vf2iu", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vf2id", &Jit::Comp_Generic, Dis_Vf2i, Int_Vf2i, IS_VFPU|OUT_EAT_PREFIX), //20 - INSTR("vi2f", &Jit::Comp_Generic, Dis_Vf2i, Int_Vi2f, IS_VFPU), - INSTR("vcmov", &Jit::Comp_Generic, Dis_Vcmov,Int_Vcmov,IS_VFPU), + INSTR("vi2f", &Jit::Comp_Generic, Dis_Vf2i, Int_Vi2f, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vcmov", &Jit::Comp_Generic, Dis_Vcmov,Int_Vcmov,IS_VFPU|OUT_EAT_PREFIX), {-2}, {-2}, @@ -546,10 +546,10 @@ const MIPSInstruction tableVFPU4Jump[32] = //110100 xxxxx const MIPSInstruction tableVFPU7[32] = { - INSTR("vrnds", &Jit::Comp_Generic, Dis_Generic, Int_Vrnds, IS_VFPU), - INSTR("vrndi", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU), - INSTR("vrndf1", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU), - INSTR("vrndf2", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU), + INSTR("vrnds", &Jit::Comp_Generic, Dis_Generic, Int_Vrnds, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vrndi", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vrndf1", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vrndf2", &Jit::Comp_Generic, Dis_Generic, Int_VrndX, IS_VFPU|OUT_EAT_PREFIX), {-2},{-2},{-2},{-2}, //8 @@ -559,8 +559,8 @@ const MIPSInstruction tableVFPU7[32] = //16 {-2}, {-2}, - INSTR("vf2h", &Jit::Comp_Generic, Dis_Generic, Int_Vf2h, IS_VFPU), - INSTR("vh2f", &Jit::Comp_Generic, Dis_Generic, Int_Vh2f, IS_VFPU), + INSTR("vf2h", &Jit::Comp_Generic, Dis_Generic, Int_Vf2h, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vh2f", &Jit::Comp_Generic, Dis_Generic, Int_Vh2f, IS_VFPU|OUT_EAT_PREFIX), {-2}, {-2}, @@ -572,41 +572,41 @@ const MIPSInstruction tableVFPU7[32] = INSTR("vus2i", &Jit::Comp_Generic, Dis_Vs2i, Int_Vx2i, IS_VFPU), INSTR("vs2i", &Jit::Comp_Generic, Dis_Vs2i, Int_Vx2i, IS_VFPU), - INSTR("vi2uc", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU), - INSTR("vi2c", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU), - INSTR("vi2us", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU), - INSTR("vi2s", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU), + INSTR("vi2uc", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vi2c", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vi2us", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vi2s", &Jit::Comp_Generic, Dis_Vi2x, Int_Vi2x, IS_VFPU|OUT_EAT_PREFIX), }; // 110100 00000 10100 0000000000000000 // 110100 00000 10111 0000000000000000 const MIPSInstruction tableVFPU4[32] = //110100 00000 xxxxx { - INSTR("vmov", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op,IS_VFPU), - INSTR("vabs", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op,IS_VFPU), - INSTR("vneg", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op,IS_VFPU), - INSTR("vidt", &Jit::Comp_Generic, Dis_VectorSet1, Int_Vidt,IS_VFPU), - INSTR("vsat0", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vsat1", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vzero", &Jit::Comp_Generic, Dis_VectorSet1, Int_VVectorInit, IS_VFPU), - INSTR("vone", &Jit::Comp_Generic, Dis_VectorSet1, Int_VVectorInit, IS_VFPU), + INSTR("vmov", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op,IS_VFPU|OUT_EAT_PREFIX), + INSTR("vabs", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op,IS_VFPU|OUT_EAT_PREFIX), + INSTR("vneg", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op,IS_VFPU|OUT_EAT_PREFIX), + INSTR("vidt", &Jit::Comp_Generic, Dis_VectorSet1, Int_Vidt,IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsat0", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsat1", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vzero", &Jit::Comp_VVectorInit, Dis_VectorSet1, Int_VVectorInit, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vone", &Jit::Comp_VVectorInit, Dis_VectorSet1, Int_VVectorInit, IS_VFPU|OUT_EAT_PREFIX), //8 {-2},{-2},{-2},{-2},{-2},{-2},{-2},{-2}, //16 - INSTR("vrcp", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vrsq", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vsin", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vcos", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vexp2", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vlog2", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vsqrt", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), - INSTR("vasin", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), + INSTR("vrcp", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vrsq", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsin", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vcos", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vexp2", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vlog2", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsqrt", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vasin", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), //24 - INSTR("vnrcp", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op,IS_VFPU), + INSTR("vnrcp", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op,IS_VFPU|OUT_EAT_PREFIX), {-2}, - INSTR("vnsin", &Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op,IS_VFPU), + INSTR("vnsin", &Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op,IS_VFPU|OUT_EAT_PREFIX), {-2}, - INSTR("vrexp2",&Jit::Comp_Generic, Dis_VectorSet2, Int_VV2Op, IS_VFPU), + INSTR("vrexp2",&Jit::Comp_VV2Op, Dis_VectorSet2, Int_VV2Op, IS_VFPU|OUT_EAT_PREFIX), {-2},{-2},{-2}, //32 }; @@ -619,42 +619,42 @@ MIPSInstruction tableVFPU5[8] = //110111 xxx INSTR("vpfxt",&Jit::Comp_VPFX, Dis_VPFXST, Int_VPFX, IS_VFPU), INSTR("vpfxd", &Jit::Comp_VPFX, Dis_VPFXD, Int_VPFX, IS_VFPU), INSTR("vpfxd", &Jit::Comp_VPFX, Dis_VPFXD, Int_VPFX, IS_VFPU), - INSTR("viim.s",&Jit::Comp_Generic, Dis_Viim,Int_Viim, IS_VFPU), - INSTR("vfim.s",&Jit::Comp_Generic, Dis_Viim,Int_Viim, IS_VFPU), + INSTR("viim.s",&Jit::Comp_Generic, Dis_Viim,Int_Viim, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vfim.s",&Jit::Comp_Generic, Dis_Viim,Int_Viim, IS_VFPU|OUT_EAT_PREFIX), }; const MIPSInstruction tableVFPU6[32] = //111100 xxx { //0 - INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU), - INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU), - INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU), - INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU), - - INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), + INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmmul",&Jit::Comp_Generic, Dis_MatrixMult, Int_Vmmul, IS_VFPU|OUT_EAT_PREFIX), + + INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm2",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), //8 - INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - - INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), - INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU), + INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm3",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + + INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), + INSTR("v(h)tfm4",&Jit::Comp_Generic, Dis_Vtfm, Int_Vtfm, IS_VFPU|OUT_EAT_PREFIX), //16 - INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU), - INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU), - INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU), - INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU), - - INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU), - INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU), - INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU), - INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU), + INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmscl",&Jit::Comp_Generic, Dis_Generic, Int_Vmscl, IS_VFPU|OUT_EAT_PREFIX), + + INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vcrsp.t/vqmul.q",&Jit::Comp_Generic, Dis_CrossQuat, Int_CrossQuat, IS_VFPU|OUT_EAT_PREFIX), //24 {-2}, {-2}, @@ -662,22 +662,22 @@ const MIPSInstruction tableVFPU6[32] = //111100 xxx {-2}, {VFPUMatrix1}, - INSTR("vrot",&Jit::Comp_Generic, Dis_VRot, Int_Vrot, IS_VFPU), + INSTR("vrot",&Jit::Comp_Generic, Dis_VRot, Int_Vrot, IS_VFPU|OUT_EAT_PREFIX), {-2}, {-2}, }; const MIPSInstruction tableVFPUMatrixSet1[16] = //111100 11100 0xxxx (rm x is 16) { - INSTR("vmmov",&Jit::Comp_Generic, Dis_MatrixSet2, Int_Vmmov, IS_VFPU), + INSTR("vmmov",&Jit::Comp_Vmmov, Dis_MatrixSet2, Int_Vmmov, IS_VFPU|OUT_EAT_PREFIX), {-2}, {-2}, - INSTR("vmidt",&Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU), + INSTR("vmidt",&Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU|OUT_EAT_PREFIX), {-2}, {-2}, - INSTR("vmzero", &Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU), - INSTR("vmone", &Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU), + INSTR("vmzero", &Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vmone", &Jit::Comp_Generic, Dis_MatrixSet1, Int_VMatrixInit, IS_VFPU|OUT_EAT_PREFIX), {-2},{-2},{-2},{-2}, {-2},{-2},{-2},{-2}, @@ -687,17 +687,17 @@ const MIPSInstruction tableVFPU9[32] = //110100 00010 xxxxx { INSTR("vsrt1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt1, IS_VFPU), INSTR("vsrt2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt2, IS_VFPU), - INSTR("vbfy1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU), - INSTR("vbfy2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU), + INSTR("vbfy1", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vbfy2", &Jit::Comp_Generic, Dis_Vbfy, Int_Vbfy, IS_VFPU|OUT_EAT_PREFIX), //4 - INSTR("vocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vocp, IS_VFPU), // one's complement - INSTR("vsocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsocp, IS_VFPU), - INSTR("vfad", &Jit::Comp_Generic, Dis_Vfad, Int_Vfad, IS_VFPU), + INSTR("vocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vocp, IS_VFPU|OUT_EAT_PREFIX), // one's complement + INSTR("vsocp", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsocp, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vfad", &Jit::Comp_Generic, Dis_Vfad, Int_Vfad, IS_VFPU|OUT_EAT_PREFIX), INSTR("vavg", &Jit::Comp_Generic, Dis_Vfad, Int_Vavg, IS_VFPU), //8 INSTR("vsrt3", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt3, IS_VFPU), INSTR("vsrt4", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsrt4, IS_VFPU), - INSTR("vsgn", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsgn, IS_VFPU), + INSTR("vsgn", &Jit::Comp_Generic, Dis_Vbfy, Int_Vsgn, IS_VFPU|OUT_EAT_PREFIX), {-2}, //12 {-2}, @@ -715,9 +715,9 @@ const MIPSInstruction tableVFPU9[32] = //110100 00010 xxxxx {-2},{-2},{-2},{-2}, //24 {-2}, - INSTR("vt4444", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU), - INSTR("vt5551", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU), - INSTR("vt5650", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU), + INSTR("vt4444", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vt5551", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU|OUT_EAT_PREFIX), + INSTR("vt5650", &Jit::Comp_Generic, Dis_Generic, Int_ColorConv, IS_VFPU|OUT_EAT_PREFIX), //28 {-2},{-2},{-2},{-2}, @@ -738,7 +738,7 @@ const MIPSInstruction tableALLEGREX0[32] = //111111 {-2}, {-2}, //20 - INSTR("bitrev",&Jit::Comp_Generic, Dis_Allegrex,Int_Allegrex, IN_RT|OUT_RD), + INSTR("bitrev",&Jit::Comp_Allegrex, Dis_Allegrex,Int_Allegrex, IN_RT|OUT_RD), {-2}, {-2}, {-2}, @@ -868,6 +868,7 @@ void MIPSCompileOp(u32 op) if (op==0) return; const MIPSInstruction *instr = MIPSGetInstruction(op); + const int info = MIPSGetInfo(op); if (instr) { if (instr->compile) @@ -877,6 +878,9 @@ void MIPSCompileOp(u32 op) ERROR_LOG(CPU,"MIPSCompileOp %08x failed",op); //MessageBox(0,"ARGH2",0,0);//compile an interpreter call } + + if (info & OUT_EAT_PREFIX) + MIPSComp::jit->EatPrefix(); } else { diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index 2d71bc73d168..68e453af9999 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -19,31 +19,34 @@ #include "../../Globals.h" -#define IS_CONDBRANCH 0x100 -#define IS_JUMP 0x200 -#define IS_VFPU 0x80000000 -#define LIKELY 0x80 -#define UNCONDITIONAL 0x40 -#define BAD_INSTRUCTION 0x20 -#define DELAYSLOT 0x10 - -#define IN_RS_ADDR 0x800 -#define IN_RS_SHIFT 0x400 -#define IN_RS 0x1000 -#define IN_RT 0x2000 -#define IN_SA 0x4000 -#define IN_IMM16 0x8000 -#define IN_IMM26 0x10000 -#define IN_MEM 0x20000 -#define IN_OTHER 0x40000 -#define IN_FPUFLAG 0x80000 - -#define OUT_RT 0x100000 -#define OUT_RD 0x200000 -#define OUT_RA 0x400000 -#define OUT_MEM 0x800000 -#define OUT_OTHER 0x1000000 -#define OUT_FPUFLAG 0x2000000 +#define DELAYSLOT 0x00000010 +#define BAD_INSTRUCTION 0x00000020 +#define UNCONDITIONAL 0x00000040 +#define LIKELY 0x00000080 +#define IS_CONDBRANCH 0x00000100 +#define IS_JUMP 0x00000200 + +#define IN_RS_SHIFT 0x00000400 +#define IN_RS_ADDR 0x00000800 +#define IN_RS 0x00001000 +#define IN_RT 0x00002000 +#define IN_SA 0x00004000 +#define IN_IMM16 0x00008000 +#define IN_IMM26 0x00010000 +#define IN_MEM 0x00020000 +#define IN_OTHER 0x00040000 +#define IN_FPUFLAG 0x00080000 + +#define OUT_RT 0x00100000 +#define OUT_RD 0x00200000 +#define OUT_RA 0x00400000 +#define OUT_MEM 0x00800000 +#define OUT_OTHER 0x01000000 +#define OUT_FPUFLAG 0x02000000 +#define OUT_EAT_PREFIX 0x04000000 + +#define VFPU_NO_PREFIX 0x08000000 +#define IS_VFPU 0x80000000 #ifndef CDECL #define CDECL diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index fd94a6cf0234..457d4310fad0 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -179,19 +179,22 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) { case M_4x4: row = (reg>>5)&2; side = 4; break; } - int transpose = (reg>>5)&1; + int transpose = (reg>>5)&1; + if (currentMIPS->VfpuWriteMask() != 0) { + ERROR_LOG(CPU, "Write mask used with vfpu matrix instruction."); + } for (int i=0; iVfpuWriteMask(i)) + if (j != side -1 || !currentMIPS->VfpuWriteMask(i)) { - int index = mtx * 4; + int index = mtx * 4; if (transpose) - index += ((row+i)&3) + ((col+j)&3)*32; - else - index += ((col+j)&3) + ((row+i)&3)*32; - V(index) = rd[j*4+i]; + index += ((row+i)&3) + ((col+j)&3)*32; + else + index += ((col+j)&3) + ((row+i)&3)*32; + V(index) = rd[j*4+i]; } } } @@ -243,6 +246,7 @@ MatrixSize GetMtxSize(u32 op) a += (b<<1); switch (a) { + case 0: ERROR_LOG(CPU, "Unexpected matrix size 1x1."); return M_2x2; case 1: return M_2x2; case 2: return M_3x3; case 3: return M_4x4; @@ -278,7 +282,10 @@ const char *GetVectorNotation(int reg, VectorSize size) case V_Quad: c='C'; row=(reg>>5)&2; break; } if (transpose && c == 'C') c='R'; - sprintf(hej[yo],"%c%i%i%i",c,mtx,col,row); + if (transpose) + sprintf(hej[yo],"%c%i%i%i",c,mtx,row,col); + else + sprintf(hej[yo],"%c%i%i%i",c,mtx,col,row); return hej[yo]; } diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 1d4367c7577a..e02d7cc9c37a 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -129,13 +129,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) SetJumpTarget(notfound); //Ok, no block, let's jit -#ifdef _M_IX86 - ABI_AlignStack(0); - CALL(reinterpret_cast(&Jit)); - ABI_RestoreStack(0); -#elif _M_X64 - CALL((void *)&Jit); -#endif + ABI_CallFunction((void *)&Jit); JMP(dispatcherNoCheck); // Let's just dispatch again, we'll enter the block since we know it's there. SetJumpTarget(bail); diff --git a/Core/MIPS/x86/CompALU.cpp b/Core/MIPS/x86/CompALU.cpp index e7da0341341b..7b447674f2eb 100644 --- a/Core/MIPS/x86/CompALU.cpp +++ b/Core/MIPS/x86/CompALU.cpp @@ -17,6 +17,7 @@ #include "Jit.h" #include "RegCache.h" +#include using namespace MIPSAnalyst; #define _RS ((op>>21) & 0x1F) @@ -77,16 +78,26 @@ namespace MIPSComp gpr.Lock(rt, rs); gpr.BindToRegister(rt, rt == rs, true); - if (rt != rs) + if (rt == rs || gpr.R(rs).IsSimpleReg()) + LEA(32, gpr.RX(rt), MDisp(gpr.RX(rs), simm)); + else + { MOV(32, gpr.R(rt), gpr.R(rs)); - if (simm != 0) - ADD(32, gpr.R(rt), Imm32((u32)(s32)simm)); - // TODO: Can also do LEA if both operands happen to be in registers. + if (simm != 0) + ADD(32, gpr.R(rt), Imm32((u32)(s32)simm)); + } gpr.UnlockAll(); } break; case 10: // R(rt) = (s32)R(rs) < simm; break; //slti + // There's a mips compiler out there asking it already knows the answer to... + if (gpr.IsImmediate(rs)) + { + gpr.SetImmediate32(rt, (s32)gpr.GetImmediate32(rs) < simm); + break; + } + gpr.Lock(rt, rs); gpr.BindToRegister(rs, true, false); gpr.BindToRegister(rt, rt == rs, true); @@ -98,6 +109,12 @@ namespace MIPSComp break; case 11: // R(rt) = R(rs) < uimm; break; //sltiu + if (gpr.IsImmediate(rs)) + { + gpr.SetImmediate32(rt, gpr.GetImmediate32(rs) < uimm); + break; + } + gpr.Lock(rt, rs); gpr.BindToRegister(rs, true, false); gpr.BindToRegister(rt, rt == rs, true); @@ -141,6 +158,29 @@ namespace MIPSComp } } + void Jit::Comp_RType2(u32 op) + { + CONDITIONAL_DISABLE; + int rs = _RS; + int rd = _RD; + + // Don't change $zr. + if (rd == 0) + return; + + switch (op & 63) + { + case 22: //clz + DISABLE; + break; + case 23: //clo + DISABLE; + break; + default: + DISABLE; + } + } + static u32 RType3_ImmAdd(const u32 a, const u32 b) { return a + b; @@ -180,17 +220,37 @@ namespace MIPSComp return; } + // Act like zero was used if the operand is equivalent. This happens. + if (gpr.IsImmediate(rs) && gpr.GetImmediate32(rs) == 0) + rs = 0; + if (gpr.IsImmediate(rt) && gpr.GetImmediate32(rt) == 0) + rt = 0; + gpr.Lock(rt, rs, rd); - // Optimize out + 0 and | 0. - if ((doImm == &RType3_ImmAdd || doImm == &RType3_ImmOr) && (rs == 0 || rt == 0)) + // Optimize out operations against 0... and is the only one that isn't a MOV. + if (rt == 0 || (rs == 0 && doImm != &RType3_ImmSub)) { - int rsource = rt == 0 ? rs : rt; - if (rsource != rd) + if (doImm == &RType3_ImmAnd) + gpr.SetImmediate32(rd, 0); + else { - gpr.BindToRegister(rd, false, true); - MOV(32, gpr.R(rd), gpr.R(rsource)); + int rsource = rt == 0 ? rs : rt; + if (rsource != rd) + { + gpr.BindToRegister(rd, false, true); + MOV(32, gpr.R(rd), gpr.R(rsource)); + } } } + else if (gpr.IsImmediate(rt)) + { + // No temporary needed. + u32 rtval = gpr.GetImmediate32(rt); + gpr.BindToRegister(rd, rs == rd, true); + if (rs != rd) + MOV(32, gpr.R(rd), gpr.R(rs)); + (this->*arith)(32, gpr.R(rd), Imm32(rtval)); + } else { // Use EAX as a temporary if we'd overwrite it. @@ -218,15 +278,63 @@ namespace MIPSComp switch (op & 63) { - //case 10: if (!R(rt)) R(rd) = R(rs); break; //movz - //case 11: if (R(rt)) R(rd) = R(rs); break; //movn + case 10: //if (R(rt) == 0) R(rd) = R(rs); break; //movz + if (rd == rs) + break; + gpr.Lock(rt, rs, rd); + if (!gpr.IsImmediate(rt)) + { + gpr.KillImmediate(rs, true, false); + // Need to load rd in case the condition fails. + gpr.BindToRegister(rd, true, true); + CMP(32, gpr.R(rt), Imm32(0)); + CMOVcc(32, gpr.RX(rd), gpr.R(rs), CC_E); + } + else if (gpr.GetImmediate32(rt) == 0) + { + // Yes, this actually happens. + if (gpr.IsImmediate(rs)) + gpr.SetImmediate32(rd, gpr.GetImmediate32(rs)); + else if (rd != rs) + { + gpr.BindToRegister(rd, false, true); + MOV(32, gpr.R(rd), gpr.R(rs)); + } + } + gpr.UnlockAll(); + break; + + case 11: //if (R(rt) != 0) R(rd) = R(rs); break; //movn + if (rd == rs) + break; + gpr.Lock(rt, rs, rd); + if (!gpr.IsImmediate(rt)) + { + gpr.KillImmediate(rs, true, false); + // Need to load rd in case the condition fails. + gpr.BindToRegister(rd, true, true); + CMP(32, gpr.R(rt), Imm32(0)); + CMOVcc(32, gpr.RX(rd), gpr.R(rs), CC_NE); + } + else if (gpr.GetImmediate32(rt) != 0) + { + if (gpr.IsImmediate(rs)) + gpr.SetImmediate32(rd, gpr.GetImmediate32(rs)); + else if (rd != rs) + { + gpr.BindToRegister(rd, false, true); + MOV(32, gpr.R(rd), gpr.R(rs)); + } + } + gpr.UnlockAll(); + break; - // case 32: //R(rd) = R(rs) + R(rt); break; //add + case 32: //R(rd) = R(rs) + R(rt); break; //add case 33: //R(rd) = R(rs) + R(rt); break; //addu CompTriArith(op, &XEmitter::ADD, &RType3_ImmAdd); break; case 34: //R(rd) = R(rs) - R(rt); break; //sub - case 35: + case 35: //R(rd) = R(rs) - R(rt); break; //subu CompTriArith(op, &XEmitter::SUB, &RType3_ImmSub); break; case 36: //R(rd) = R(rs) & R(rt); break; //and @@ -248,45 +356,111 @@ namespace MIPSComp break; case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt - gpr.Lock(rt, rs, rd); - gpr.BindToRegister(rs, true, true); - gpr.BindToRegister(rd, true, true); - XOR(32, R(EAX), R(EAX)); - CMP(32, gpr.R(rs), gpr.R(rt)); - SETcc(CC_L, R(EAX)); - MOV(32, gpr.R(rd), R(EAX)); - gpr.UnlockAll(); + if (gpr.IsImmediate(rs) && gpr.IsImmediate(rt)) + gpr.SetImmediate32(rd, (s32)gpr.GetImmediate32(rs) < (s32)gpr.GetImmediate32(rt)); + else + { + gpr.Lock(rt, rs, rd); + gpr.BindToRegister(rs, true, false); + gpr.BindToRegister(rd, rd == rt, true); + XOR(32, R(EAX), R(EAX)); + CMP(32, gpr.R(rs), gpr.R(rt)); + SETcc(CC_L, R(EAX)); + MOV(32, gpr.R(rd), R(EAX)); + gpr.UnlockAll(); + } break; case 43: //R(rd) = R(rs) < R(rt); break; //sltu - gpr.Lock(rd, rs, rt); - gpr.BindToRegister(rs, true, true); - gpr.BindToRegister(rd, true, true); - XOR(32, R(EAX), R(EAX)); - CMP(32, gpr.R(rs), gpr.R(rt)); - SETcc(CC_B, R(EAX)); - MOV(32, gpr.R(rd), R(EAX)); - gpr.UnlockAll(); + if (gpr.IsImmediate(rs) && gpr.IsImmediate(rt)) + gpr.SetImmediate32(rd, gpr.GetImmediate32(rs) < gpr.GetImmediate32(rt)); + else + { + gpr.Lock(rd, rs, rt); + gpr.BindToRegister(rs, true, false); + gpr.BindToRegister(rd, rd == rt, true); + XOR(32, R(EAX), R(EAX)); + CMP(32, gpr.R(rs), gpr.R(rt)); + SETcc(CC_B, R(EAX)); + MOV(32, gpr.R(rd), R(EAX)); + gpr.UnlockAll(); + } break; - // case 44: R(rd) = (R(rs) > R(rt)) ? R(rs) : R(rt); break; //max - // CMP(a,b); CMOVLT(a,b) + case 44: //R(rd) = (R(rs) > R(rt)) ? R(rs) : R(rt); break; //max + if (gpr.IsImmediate(rs) && gpr.IsImmediate(rt)) + gpr.SetImmediate32(rd, std::max((s32)gpr.GetImmediate32(rs), (s32)gpr.GetImmediate32(rt))); + else + { + int rsrc = rd == rt ? rs : rt; + gpr.Lock(rd, rs, rt); + gpr.KillImmediate(rsrc, true, false); + gpr.BindToRegister(rd, rd == rs || rd == rt, true); + if (rd != rt && rd != rs) + MOV(32, gpr.R(rd), gpr.R(rs)); + CMP(32, gpr.R(rd), gpr.R(rsrc)); + CMOVcc(32, gpr.RX(rd), gpr.R(rsrc), CC_L); + gpr.UnlockAll(); + } + break; + + case 45: //R(rd) = (R(rs) < R(rt)) ? R(rs) : R(rt); break; //min + if (gpr.IsImmediate(rs) && gpr.IsImmediate(rt)) + gpr.SetImmediate32(rd, std::min((s32)gpr.GetImmediate32(rs), (s32)gpr.GetImmediate32(rt))); + else + { + int rsrc = rd == rt ? rs : rt; + gpr.Lock(rd, rs, rt); + gpr.KillImmediate(rsrc, true, false); + gpr.BindToRegister(rd, rd == rs || rd == rt, true); + if (rd != rt && rd != rs) + MOV(32, gpr.R(rd), gpr.R(rs)); + CMP(32, gpr.R(rd), gpr.R(rsrc)); + CMOVcc(32, gpr.RX(rd), gpr.R(rsrc), CC_G); + gpr.UnlockAll(); + } + break; - // case 45: R(rd) = (R(rs) < R(rt)) ? R(rs) : R(rt); break; //min - // CMP(a,b); CMOVGT(a,b) default: Comp_Generic(op); break; } } + static u32 ShiftType_ImmLogicalLeft(const u32 a, const u32 b) + { + return a << (b & 0x1f); + } + + static u32 ShiftType_ImmLogicalRight(const u32 a, const u32 b) + { + return a >> (b & 0x1f); + } + + static u32 ShiftType_ImmArithRight(const u32 a, const u32 b) + { + return ((s32) a) >> (b & 0x1f); + } + + static u32 ShiftType_ImmRotateRight(const u32 a, const u32 b) + { + const s8 sa = b & 0x1f; + return (a >> sa) | (a << (32 - sa)); + } - void Jit::CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)) + void Jit::CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg), u32 (*doImm)(const u32, const u32)) { int rd = _RD; int rt = _RT; - gpr.Lock(rd, rt); int sa = _SA; + + if (doImm && gpr.IsImmediate(rt)) + { + gpr.SetImmediate32(rd, doImm(gpr.GetImmediate32(rt), sa)); + return; + } + + gpr.Lock(rd, rt); gpr.BindToRegister(rd, rd == rt, true); if (rd != rt) MOV(32, gpr.R(rd), gpr.R(rt)); @@ -295,38 +469,56 @@ namespace MIPSComp } // "over-shifts" work the same as on x86 - only bottom 5 bits are used to get the shift value - void Jit::CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)) + void Jit::CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg), u32 (*doImm)(const u32, const u32)) { int rd = _RD; int rt = _RT; int rs = _RS; - gpr.FlushLockX(ECX); + + if (doImm && gpr.IsImmediate(rs) && gpr.IsImmediate(rt)) + { + gpr.SetImmediate32(rd, doImm(gpr.GetImmediate32(rt), gpr.GetImmediate32(rs))); + return; + } + gpr.Lock(rd, rt, rs); - gpr.BindToRegister(rd, rd == rt || rd == rs, true); - MOV(32, R(ECX), gpr.R(rs)); // Only ECX can be used for variable shifts. - AND(32, R(ECX), Imm32(0x1f)); - if (rd != rt) - MOV(32, gpr.R(rd), gpr.R(rt)); - (this->*shift)(32, gpr.R(rd), R(ECX)); + if (gpr.IsImmediate(rs)) + { + int sa = gpr.GetImmediate32(rs); + gpr.BindToRegister(rd, rd == rt, true); + if (rd != rt) + MOV(32, gpr.R(rd), gpr.R(rt)); + (this->*shift)(32, gpr.R(rd), Imm8(sa)); + } + else + { + gpr.FlushLockX(ECX); + gpr.BindToRegister(rd, rd == rt || rd == rs, true); + MOV(32, R(ECX), gpr.R(rs)); // Only ECX can be used for variable shifts. + AND(32, R(ECX), Imm32(0x1f)); + if (rd != rt) + MOV(32, gpr.R(rd), gpr.R(rt)); + (this->*shift)(32, gpr.R(rd), R(ECX)); + gpr.UnlockAllX(); + } gpr.UnlockAll(); - gpr.UnlockAllX(); } void Jit::Comp_ShiftType(u32 op) { - CONDITIONAL_DISABLE + CONDITIONAL_DISABLE; int rs = _RS; int fd = _FD; // WARNIGN : ROTR switch (op & 0x3f) { - case 0: CompShiftImm(op, &XEmitter::SHL); break; - case 2: CompShiftImm(op, rs == 1 ? &XEmitter::ROR : &XEmitter::SHR); break; // srl, rotr - case 3: CompShiftImm(op, &XEmitter::SAR); break; // sra + case 0: CompShiftImm(op, &XEmitter::SHL, &ShiftType_ImmLogicalLeft); break; + case 2: CompShiftImm(op, rs == 1 ? &XEmitter::ROR : &XEmitter::SHR, rs == 1 ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; // srl, rotr + case 3: CompShiftImm(op, &XEmitter::SAR, &ShiftType_ImmArithRight); break; // sra - case 4: CompShiftVar(op, &XEmitter::SHL); break; //sllv - case 6: CompShiftVar(op, fd == 1 ? &XEmitter::ROR : &XEmitter::SHR); break; //srlv - case 7: CompShiftVar(op, &XEmitter::SAR); break; //srav + case 4: CompShiftVar(op, &XEmitter::SHL, &ShiftType_ImmLogicalLeft); break; //sllv + case 6: CompShiftVar(op, fd == 1 ? &XEmitter::ROR : &XEmitter::SHR, fd == 1 ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; //srlv + case 7: CompShiftVar(op, &XEmitter::SAR, &ShiftType_ImmArithRight); break; //srav default: Comp_Generic(op); @@ -337,8 +529,69 @@ namespace MIPSComp void Jit::Comp_Special3(u32 op) { - // ext, ins - DISABLE; + CONDITIONAL_DISABLE; + int rs = _RS; + int rt = _RT; + int pos = _POS; + + int size = _SIZE + 1; + u32 mask = 0xFFFFFFFFUL >> (32 - size); + + // Don't change $zr. + if (rt == 0) + return; + + switch (op & 0x3f) + { + case 0x0: //ext + if (gpr.IsImmediate(rs)) + { + gpr.SetImmediate32(rt, (gpr.GetImmediate32(rs) >> pos) & mask); + return; + } + + gpr.Lock(rs, rt); + gpr.BindToRegister(rt, rs == rt, true); + if (rs != rt) + MOV(32, gpr.R(rt), gpr.R(rs)); + SHR(32, gpr.R(rt), Imm8(pos)); + AND(32, gpr.R(rt), Imm32(mask)); + gpr.UnlockAll(); + break; + + case 0x4: //ins + { + u32 sourcemask = mask >> pos; + u32 destmask = ~(sourcemask << pos); + if (gpr.IsImmediate(rs)) + { + u32 inserted = (gpr.GetImmediate32(rs) & sourcemask) << pos; + if (gpr.IsImmediate(rt)) + { + gpr.SetImmediate32(rt, (gpr.GetImmediate32(rt) & destmask) | inserted); + return; + } + + gpr.Lock(rs, rt); + gpr.BindToRegister(rt, true, true); + AND(32, gpr.R(rt), Imm32(destmask)); + OR(32, gpr.R(rt), Imm32(inserted)); + gpr.UnlockAll(); + } + else + { + gpr.Lock(rs, rt); + gpr.BindToRegister(rt, true, true); + MOV(32, R(EAX), gpr.R(rs)); + AND(32, R(EAX), Imm32(sourcemask)); + SHL(32, R(EAX), Imm8(pos)); + AND(32, gpr.R(rt), Imm32(destmask)); + OR(32, gpr.R(rt), R(EAX)); + gpr.UnlockAll(); + } + } + break; + } } @@ -350,23 +603,98 @@ namespace MIPSComp switch ((op >> 6) & 31) { case 16: // seb // R(rd) = (u32)(s32)(s8)(u8)R(rt); + if (gpr.IsImmediate(rt)) + { + gpr.SetImmediate32(rd, (u32)(s32)(s8)(u8)gpr.GetImmediate32(rt)); + break; + } + gpr.Lock(rd, rt); - gpr.BindToRegister(rd, true, true); - MOV(32, R(EAX), gpr.R(rt)); // work around the byte-register addressing problem - MOVSX(32, 8, gpr.RX(rd), R(EAX)); + gpr.BindToRegister(rd, rd == rt, true); +#ifdef _M_IX86 + // work around the byte-register addressing problem + if (!gpr.R(rt).IsSimpleReg(EDX) && !gpr.R(rt).IsSimpleReg(ECX)) + { + MOV(32, R(EAX), gpr.R(rt)); + MOVSX(32, 8, gpr.RX(rd), R(EAX)); + } + else +#endif + { + gpr.KillImmediate(rt, true, false); + MOVSX(32, 8, gpr.RX(rd), gpr.R(rt)); + } gpr.UnlockAll(); break; - case 24: // seh + case 20: //bitrev + if (gpr.IsImmediate(rt)) + { + // http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel + u32 v = gpr.GetImmediate32(rt); + // swap odd and even bits + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); + // swap consecutive pairs + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); + // swap nibbles ... + v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); + // swap bytes + v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); + // swap 2-byte long pairs + v = ( v >> 16 ) | ( v << 16); + gpr.SetImmediate32(rd, v); + break; + } + gpr.Lock(rd, rt); - // MOVSX doesn't like immediate arguments, for example, so let's force it to a register. - gpr.BindToRegister(rt, true, false); - gpr.BindToRegister(rd, true, true); + gpr.BindToRegister(rd, rd == rt, true); + if (rd != rt) + MOV(32, gpr.R(rd), gpr.R(rt)); + + LEA(32, EAX, MScaled(gpr.RX(rd), 2, 0)); + SHR(32, gpr.R(rd), Imm8(1)); + XOR(32, gpr.R(rd), R(EAX)); + AND(32, gpr.R(rd), Imm32(0x55555555)); + XOR(32, gpr.R(rd), R(EAX)); + + LEA(32, EAX, MScaled(gpr.RX(rd), 4, 0)); + SHR(32, gpr.R(rd), Imm8(2)); + XOR(32, gpr.R(rd), R(EAX)); + AND(32, gpr.R(rd), Imm32(0x33333333)); + XOR(32, gpr.R(rd), R(EAX)); + + MOV(32, R(EAX), gpr.R(rd)); + SHL(32, R(EAX), Imm8(4)); + SHR(32, gpr.R(rd), Imm8(4)); + XOR(32, gpr.R(rd), R(EAX)); + AND(32, gpr.R(rd), Imm32(0x0F0F0F0F)); + XOR(32, gpr.R(rd), R(EAX)); + + MOV(32, R(EAX), gpr.R(rd)); + SHL(32, R(EAX), Imm8(8)); + SHR(32, gpr.R(rd), Imm8(8)); + XOR(32, gpr.R(rd), R(EAX)); + AND(32, gpr.R(rd), Imm32(0x00FF00FF)); + XOR(32, gpr.R(rd), R(EAX)); + + ROL(32, gpr.R(rd), Imm8(16)); + + gpr.UnlockAll(); + break; + + case 24: // seh // R(rd) = (u32)(s32)(s16)(u16)R(rt); + if (gpr.IsImmediate(rt)) + { + gpr.SetImmediate32(rd, (u32)(s32)(s16)(u16)gpr.GetImmediate32(rt)); + break; + } + + gpr.Lock(rd, rt); + gpr.BindToRegister(rd, rd == rt, true); MOVSX(32, 16, gpr.RX(rd), gpr.R(rt)); gpr.UnlockAll(); break; - case 20: //bitrev default: Comp_Generic(op); return; @@ -376,6 +704,7 @@ namespace MIPSComp void Jit::Comp_MulDivType(u32 op) { + CONDITIONAL_DISABLE; int rt = _RT; int rs = _RS; int rd = _RD; diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 1ac36e07999d..d5c76463aab8 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -144,7 +144,7 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) if (rt == 0) { - gpr.KillImmediate(rs, true, true); + gpr.KillImmediate(rs, true, false); CMP(32, gpr.R(rs), Imm32(0)); } else diff --git a/Core/MIPS/x86/CompFPU.cpp b/Core/MIPS/x86/CompFPU.cpp index bee31435cc9f..620fdc216a16 100644 --- a/Core/MIPS/x86/CompFPU.cpp +++ b/Core/MIPS/x86/CompFPU.cpp @@ -147,6 +147,7 @@ void Jit::Comp_FPULS(u32 op) } } +static const u64 GC_ALIGNED16(ssOneBits[2]) = {0x0000000100000001ULL, 0x0000000100000001ULL}; static const u64 GC_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL}; static const u64 GC_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL}; diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index b011c6ae52c3..86417d2b0b11 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -18,19 +18,20 @@ #include "../../MemMap.h" #include "../../Config.h" #include "../MIPSAnalyst.h" +#include "Core/Reporting.h" #include "Jit.h" #include "../MIPSVFPUUtils.h" #include "RegCache.h" -// VERY UNFINISHED +// VERY UNFINISHED! // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. // Currently known non working ones should have DISABLE. -// #define CONDITIONAL_DISABLE { Comp_Generic(op); return; } +// #define CONDITIONAL_DISABLE { fpr.ReleaseSpillLocks(); Comp_Generic(op); return; } #define CONDITIONAL_DISABLE ; -#define DISABLE { Comp_Generic(op); return; } +#define DISABLE { fpr.ReleaseSpillLocks(); Comp_Generic(op); return; } #define _RS ((op>>21) & 0x1F) @@ -75,9 +76,6 @@ void Jit::Comp_VPFX(u32 op) } } - -// TODO: Got register value ownership issues. We need to be sure that if we modify input -// like this, it does NOT get written back! void Jit::ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz) { if (prefix == 0xE4) return; @@ -86,10 +84,7 @@ void Jit::ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz) { static const float constantArray[8] = {0.f, 1.f, 2.f, 0.5f, 3.f, 1.f/3.f, 0.25f, 1.f/6.f}; for (int i = 0; i < n; i++) - { - // TODO: This needs to be the original values, not the original regs. (e.g. [-x, |x|, x]) origV[i] = vregs[i]; - } for (int i = 0; i < n; i++) { @@ -98,55 +93,83 @@ void Jit::ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz) { int negate = (prefix >> (16+i)) & 1; int constants = (prefix >> (12+i)) & 1; + // Unchanged, hurray. + if (!constants && regnum == i && !abs && !negate) + continue; + + // This puts the value into a temp reg, so we won't write the modified value back. + vregs[i] = fpr.GetTempV(); + fpr.MapRegV(vregs[i], MAP_NOINIT | MAP_DIRTY); + if (!constants) { // Prefix may say "z, z, z, z" but if this is a pair, we force to x. // TODO: But some ops seem to use const 0 instead? - if (regnum > n) { + if (regnum >= n) { + ERROR_LOG(CPU, "Invalid VFPU swizzle: %08x / %d", prefix, sz); + Reporting::ReportMessage("Invalid VFPU swizzle: %08x / %d", prefix, sz); regnum = 0; } - vregs[i] = origV[regnum]; + MOVSS(fpr.VX(vregs[i]), fpr.V(origV[regnum])); if (abs) { ANDPS(fpr.VX(vregs[i]), M((void *)&noSignMask)); } - } else { + } else { MOVSS(fpr.VX(vregs[i]), M((void *)&constantArray[regnum + (abs<<2)])); } if (negate) XORPS(fpr.VX(vregs[i]), M((void *)&signBitLower)); + + // TODO: This probably means it will swap out soon, inefficiently... + fpr.ReleaseSpillLockV(vregs[i]); + } +} + +void Jit::GetVectorRegsPrefixD(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixDFlag & JitState::PREFIX_KNOWN); + + GetVectorRegs(regs, sz, vectorReg); + if (js.prefixD == 0) + return; + + int n = GetNumVectorElements(sz); + for (int i = 0; i < n; i++) + { + // Hopefully this is rare, we'll just write it into a reg we drop. + if (js.VfpuWriteMask(i)) + regs[i] = fpr.GetTempV(); } } -void Jit::ApplyPrefixD(const u8 *vregs, u32 prefix, VectorSize sz, bool onlyWriteMask) { +void Jit::ApplyPrefixD(const u8 *vregs, VectorSize sz) { _assert_(js.prefixDFlag & JitState::PREFIX_KNOWN); - if (!prefix) return; + if (!js.prefixD) return; int n = GetNumVectorElements(sz); for (int i = 0; i < n; i++) { - int mask = (prefix >> (8 + i)) & 1; - js.writeMask[i] = mask ? true : false; - if (onlyWriteMask) + if (js.VfpuWriteMask(i)) continue; - if (!mask) { - int sat = (prefix >> (i * 2)) & 3; - if (sat == 1) - { - MAXSS(fpr.VX(vregs[i]), M((void *)&zero)); - MINSS(fpr.VX(vregs[i]), M((void *)&one)); - } - else if (sat == 3) - { - MAXSS(fpr.VX(vregs[i]), M((void *)&minus_one)); - MINSS(fpr.VX(vregs[i]), M((void *)&one)); - } + + int sat = (js.prefixD >> (i * 2)) & 3; + if (sat == 1) + { + fpr.MapRegV(vregs[i], MAP_DIRTY); + MAXSS(fpr.VX(vregs[i]), M((void *)&zero)); + MINSS(fpr.VX(vregs[i]), M((void *)&one)); + } + else if (sat == 3) + { + fpr.MapRegV(vregs[i], MAP_DIRTY); + MAXSS(fpr.VX(vregs[i]), M((void *)&minus_one)); + MINSS(fpr.VX(vregs[i]), M((void *)&one)); } } } // Vector regs can overlap in all sorts of swizzled ways. // This does allow a single overlap in sregs[i]. -bool IsOverlapSafeAllowS(int dreg, int di, int sn, u8 sregs[], int tn, u8 tregs[]) +bool IsOverlapSafeAllowS(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[] = NULL) { for (int i = 0; i < sn; ++i) { @@ -163,7 +186,7 @@ bool IsOverlapSafeAllowS(int dreg, int di, int sn, u8 sregs[], int tn, u8 tregs[ return true; } -bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn, u8 tregs[]) +bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[] = NULL) { return IsOverlapSafeAllowS(dreg, di, sn, sregs, tn, tregs) && sregs[di] != dreg; } @@ -315,30 +338,66 @@ void Jit::Comp_SVQ(u32 op) } } -void Jit::Comp_VDot(u32 op) { - DISABLE; +void Jit::Comp_VVectorInit(u32 op) { + CONDITIONAL_DISABLE; - // WARNING: No prefix support! - if (js.MayHavePrefix()) { - Comp_Generic(op); - js.EatPrefix(); - return; + if (js.HasUnknownPrefix()) + DISABLE; + + switch ((op >> 16) & 0xF) + { + case 6: // v=zeros; break; //vzero + MOVSS(XMM0, M((void *) &zero)); + break; + case 7: // v=ones; break; //vone + MOVSS(XMM0, M((void *) &one)); + break; + default: + DISABLE; + break; } - int vd = _VD; - int vs = _VS; - int vt = _VT; VectorSize sz = GetVecSize(op); - - // TODO: Force read one of them into regs? probably not. - u8 sregs[4], tregs[4], dregs[4]; - GetVectorRegs(sregs, sz, vs); - GetVectorRegs(tregs, sz, vt); - GetVectorRegs(dregs, sz, vd); + int n = GetNumVectorElements(sz); + u8 dregs[4]; + GetVectorRegsPrefixD(dregs, sz, _VD); + fpr.MapRegsV(dregs, sz, MAP_NOINIT | MAP_DIRTY); + for (int i = 0; i < n; ++i) + { + switch ((op >> 16) & 0xF) + { + case 6: // v=zeros; break; //vzero + MOVSS(fpr.VX(dregs[i]), R(XMM0)); + break; + case 7: // v=ones; break; //vone + MOVSS(fpr.VX(dregs[i]), R(XMM0)); + break; + default: + DISABLE; + break; + } + } + + ApplyPrefixD(dregs, sz); + + fpr.ReleaseSpillLocks(); +} - // TODO: applyprefixST here somehow (shuffle, etc...) +void Jit::Comp_VDot(u32 op) { + CONDITIONAL_DISABLE; + + if (js.HasUnknownPrefix()) + DISABLE; + VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); + + // TODO: Force read one of them into regs? probably not. + u8 sregs[4], tregs[4], dregs[1]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixT(tregs, sz, _VT); + GetVectorRegsPrefixD(dregs, V_Single, _VD); + X64Reg tempxreg = XMM0; if (IsOverlapSafe(dregs[0], 0, n, sregs, n, tregs)) { @@ -347,7 +406,7 @@ void Jit::Comp_VDot(u32 op) { } // Need to start with +0.0f so it doesn't result in -0.0f. - MOVSS(tempxreg, M((void *) &zero)); + XORPS(tempxreg, R(tempxreg)); for (int i = 0; i < n; i++) { // sum += s[i]*t[i]; @@ -362,33 +421,16 @@ void Jit::Comp_VDot(u32 op) { MOVSS(fpr.V(dregs[0]), tempxreg); } - // TODO: applyprefixD here somehow (write mask etc..) + ApplyPrefixD(dregs, V_Single); fpr.ReleaseSpillLocks(); - - js.EatPrefix(); } void Jit::Comp_VecDo3(u32 op) { - DISABLE; - - // WARNING: No prefix support! - if (js.MayHavePrefix()) - { - Comp_Generic(op); - js.EatPrefix(); - return; - } - - int vd = _VD; - int vs = _VS; - int vt = _VT; - VectorSize sz = GetVecSize(op); + CONDITIONAL_DISABLE; - u8 sregs[4], tregs[4], dregs[4]; - GetVectorRegs(sregs, sz, vs); - GetVectorRegs(tregs, sz, vt); - GetVectorRegs(dregs, sz, vd); + if (js.HasUnknownPrefix()) + DISABLE; void (XEmitter::*xmmop)(X64Reg, OpArg) = NULL; switch (op >> 26) @@ -418,14 +460,16 @@ void Jit::Comp_VecDo3(u32 op) { } if (xmmop == NULL) - { - Comp_Generic(op); - js.EatPrefix(); - return; - } + DISABLE; + VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); + u8 sregs[4], tregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixT(tregs, sz, _VT); + GetVectorRegsPrefixD(dregs, sz, _VD); + X64Reg tempxregs[4]; for (int i = 0; i < n; ++i) { @@ -436,9 +480,10 @@ void Jit::Comp_VecDo3(u32 op) { tempxregs[i] = (X64Reg) (XMM0 + i); else { - fpr.BindToRegister(TEMP0 + i, false, true); - fpr.SpillLock(TEMP0 + i); - tempxregs[i] = fpr.RX(TEMP0 + i); + int reg = fpr.GetTempV(); + fpr.MapRegV(reg, MAP_NOINIT | MAP_DIRTY); + fpr.SpillLockV(reg); + tempxregs[i] = fpr.VX(reg); } } else @@ -462,9 +507,128 @@ void Jit::Comp_VecDo3(u32 op) { MOVSS(fpr.V(dregs[i]), tempxregs[i]); } + ApplyPrefixD(dregs, sz); + fpr.ReleaseSpillLocks(); +} + +void Jit::Comp_VV2Op(u32 op) { + CONDITIONAL_DISABLE; + + if (js.HasUnknownPrefix()) + DISABLE; + + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + u8 sregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixD(dregs, sz, _VD); + + X64Reg tempxregs[4]; + for (int i = 0; i < n; ++i) + { + if (!IsOverlapSafeAllowS(dregs[i], i, n, sregs)) + { + int reg = fpr.GetTempV(); + fpr.MapRegV(reg, MAP_NOINIT | MAP_DIRTY); + fpr.SpillLockV(reg); + tempxregs[i] = fpr.VX(reg); + } + else + { + fpr.MapRegV(dregs[i], (dregs[i] == sregs[i] ? 0 : MAP_NOINIT) | MAP_DIRTY); + fpr.SpillLockV(dregs[i]); + tempxregs[i] = fpr.VX(dregs[i]); + } + } + + // Warning: sregs[i] and tempxregs[i] may be the same reg. + // Helps for vmov, hurts for vrcp, etc. + for (int i = 0; i < n; ++i) + { + switch ((op >> 16) & 0x1f) + { + case 0: // d[i] = s[i]; break; //vmov + // Probably for swizzle. + if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(tempxregs[i], fpr.V(sregs[i])); + break; + case 1: // d[i] = fabsf(s[i]); break; //vabs + if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(tempxregs[i], fpr.V(sregs[i])); + ANDPS(tempxregs[i], M((void *)&noSignMask)); + break; + case 2: // d[i] = -s[i]; break; //vneg + if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(tempxregs[i], fpr.V(sregs[i])); + XORPS(tempxregs[i], M((void *)&signBitLower)); + break; + case 4: // if (s[i] < 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0 + if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(tempxregs[i], fpr.V(sregs[i])); + // TODO: Doesn't handle NaN correctly. + MAXSS(tempxregs[i], M((void *)&zero)); + MINSS(tempxregs[i], M((void *)&one)); + break; + case 5: // if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1 + if (!fpr.V(sregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(tempxregs[i], fpr.V(sregs[i])); + // TODO: Doesn't handle NaN correctly. + MAXSS(tempxregs[i], M((void *)&minus_one)); + MINSS(tempxregs[i], M((void *)&one)); + break; + case 16: // d[i] = 1.0f / s[i]; break; //vrcp + MOVSS(XMM0, M((void *)&one)); + DIVSS(XMM0, fpr.V(sregs[i])); + MOVSS(tempxregs[i], R(XMM0)); + break; + case 17: // d[i] = 1.0f / sqrtf(s[i]); break; //vrsq + SQRTSS(XMM0, fpr.V(sregs[i])); + MOVSS(tempxregs[i], M((void *)&one)); + DIVSS(tempxregs[i], R(XMM0)); + break; + case 18: // d[i] = sinf((float)M_PI_2 * s[i]); break; //vsin + DISABLE; + break; + case 19: // d[i] = cosf((float)M_PI_2 * s[i]); break; //vcos + DISABLE; + break; + case 20: // d[i] = powf(2.0f, s[i]); break; //vexp2 + DISABLE; + break; + case 21: // d[i] = logf(s[i])/log(2.0f); break; //vlog2 + DISABLE; + break; + case 22: // d[i] = sqrtf(s[i]); break; //vsqrt + SQRTSS(tempxregs[i], fpr.V(sregs[i])); + ANDPS(tempxregs[i], M((void *)&noSignMask)); + break; + case 23: // d[i] = asinf(s[i] * (float)M_2_PI); break; //vasin + DISABLE; + break; + case 24: // d[i] = -1.0f / s[i]; break; // vnrcp + MOVSS(XMM0, M((void *)&minus_one)); + DIVSS(XMM0, fpr.V(sregs[i])); + MOVSS(tempxregs[i], R(XMM0)); + break; + case 26: // d[i] = -sinf((float)M_PI_2 * s[i]); break; // vnsin + DISABLE; + break; + case 28: // d[i] = 1.0f / expf(s[i] * (float)M_LOG2E); break; // vrexp2 + DISABLE; + break; + } + } + for (int i = 0; i < n; ++i) + { + if (!fpr.V(dregs[i]).IsSimpleReg(tempxregs[i])) + MOVSS(fpr.V(dregs[i]), tempxregs[i]); + } + + ApplyPrefixD(dregs, sz); - js.EatPrefix(); + fpr.ReleaseSpillLocks(); } void Jit::Comp_Mftv(u32 op) { @@ -528,7 +692,7 @@ void Jit::Comp_Vmtvc(u32 op) { int imm = op & 0xFF; if (imm >= 128 && imm < 128 + VFPU_CTRL_MAX) { fpr.MapRegV(vs, 0); - MOVSS(M(¤tMIPS->vfpuCtrl[imm - 128]), fpr.RX(vs)); + MOVSS(M(¤tMIPS->vfpuCtrl[imm - 128]), fpr.VX(vs)); fpr.ReleaseSpillLocks(); if (imm - 128 == VFPU_CTRL_SPREFIX) { @@ -541,4 +705,46 @@ void Jit::Comp_Vmtvc(u32 op) { } } -} \ No newline at end of file +void Jit::Comp_Vmmov(u32 op) { + CONDITIONAL_DISABLE; + + // TODO: This probably ignores prefixes? + if (js.MayHavePrefix()) + DISABLE; + + MatrixSize sz = GetMtxSize(op); + int n = GetMatrixSide(sz); + + u8 sregs[16], dregs[16]; + GetMatrixRegs(sregs, sz, _VS); + GetMatrixRegs(dregs, sz, _VD); + + // TODO: gas doesn't allow overlap, what does the PSP do? + // Potentially detect overlap or the safe direction to move in, or just DISABLE? + // This is very not optimal, blows the regcache everytime. + u8 tempregs[16]; + for (int a = 0; a < n; a++) + { + for (int b = 0; b < n; b++) + { + u8 temp = (u8) fpr.GetTempV(); + fpr.MapRegV(temp, MAP_NOINIT | MAP_DIRTY); + MOVSS(fpr.VX(temp), fpr.V(sregs[a * 4 + b])); + fpr.StoreFromRegisterV(temp); + tempregs[a * 4 + b] = temp; + } + } + for (int a = 0; a < n; a++) + { + for (int b = 0; b < n; b++) + { + u8 temp = tempregs[a * 4 + b]; + fpr.MapRegV(temp, 0); + MOVSS(fpr.V(dregs[a * 4 + b]), fpr.VX(temp)); + } + } + + fpr.ReleaseSpillLocks(); +} + +} diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 9fb2487713e0..6a5bb0ac54ca 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -17,6 +17,7 @@ #include #include +#include "Common/ChunkFile.h" #include "../../Core.h" #include "../../CoreTiming.h" #include "../../Config.h" @@ -103,6 +104,15 @@ Jit::Jit(MIPSState *mips) : blocks(mips), mips_(mips) gpr.SetEmitter(this); fpr.SetEmitter(this); AllocCodeSpace(1024 * 1024 * 16); + + // TODO: If it becomes possible to switch from the interpreter, this should be set right. + js.startDefaultPrefix = true; +} + +void Jit::DoState(PointerWrap &p) +{ + p.Do(js.startDefaultPrefix); + p.DoMarker("Jit"); } void Jit::FlushAll() @@ -202,6 +212,17 @@ void Jit::Compile(u32 em_address) int block_num = blocks.AllocateBlock(em_address); JitBlock *b = blocks.GetBlock(block_num); blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, b)); + + // Drat. The VFPU hit an uneaten prefix at the end of a block. + if (js.startDefaultPrefix && js.MayHavePrefix()) + { + js.startDefaultPrefix = false; + // Our assumptions are all wrong so it's clean-slate time. + ClearCache(); + + // Let's try that one more time. We won't get back here because we toggled the value. + Compile(em_address); + } } void Jit::RunLoopUntil(u64 globalticks) @@ -282,9 +303,13 @@ void Jit::Comp_Generic(u32 op) else _dbg_assert_msg_(JIT, 0, "Trying to compile instruction that can't be interpreted"); - // Might have eaten prefixes, hard to tell... - if ((MIPSGetInfo(op) & IS_VFPU) != 0) - js.PrefixStart(); + const int info = MIPSGetInfo(op); + if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) + { + // If it does eat them, it'll happen in MIPSCompileOp(). + if ((info & OUT_EAT_PREFIX) == 0) + js.PrefixUnknown(); + } } void Jit::WriteExit(u32 destination, int exit_num) @@ -323,6 +348,8 @@ void Jit::WriteExitDestInEAX() CMP(32, R(EAX), Imm32(PSP_GetUserMemoryEnd())); FixupBranch tooHigh = J_CC(CC_GE); + // Need to set neg flag again if necessary. + SUB(32, M(¤tMIPS->downcount), Imm32(0)); JMP(asm_.dispatcher, true); SetJumpTarget(tooLow); @@ -330,12 +357,18 @@ void Jit::WriteExitDestInEAX() ABI_CallFunctionA(thunks.ProtectFunction((void *) Memory::GetPointer, 1), R(EAX)); CMP(32, R(EAX), Imm32(0)); - J_CC(CC_NE, asm_.dispatcher, true); + FixupBranch skip = J_CC(CC_NE); // TODO: "Ignore" this so other threads can continue? if (g_Config.bIgnoreBadMemAccess) - MOV(32, M((void*)&coreState), Imm32(CORE_ERROR)); + ABI_CallFunctionA(thunks.ProtectFunction((void *) Core_UpdateState, 1), Imm32(CORE_ERROR)); + + SUB(32, M(¤tMIPS->downcount), Imm32(0)); JMP(asm_.dispatcherCheckCoreState, true); + SetJumpTarget(skip); + + SUB(32, M(¤tMIPS->downcount), Imm32(0)); + J_CC(CC_NE, asm_.dispatcher, true); } else JMP(asm_.dispatcher, true); @@ -353,7 +386,7 @@ bool Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) { FlushAll(); MOV(32, M(&mips_->pc), Imm32(js.compilerPC)); - CALL((void *)&JitBreakpoint); + ABI_CallFunction((void *)&JitBreakpoint); WriteDowncount(downcountOffset); JMP(asm_.dispatcherCheckCoreState, true); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 2d5f9ef3c1c8..f5bf0bdf538b 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -66,29 +66,42 @@ struct JitState JitBlock *curBlock; // VFPU prefix magic + bool startDefaultPrefix; u32 prefixS; u32 prefixT; u32 prefixD; - bool writeMask[4]; PrefixState prefixSFlag; PrefixState prefixTFlag; PrefixState prefixDFlag; void PrefixStart() { + if (startDefaultPrefix) { + EatPrefix(); + } else { + PrefixUnknown(); + } + } + void PrefixUnknown() { prefixSFlag = PREFIX_UNKNOWN; prefixTFlag = PREFIX_UNKNOWN; prefixDFlag = PREFIX_UNKNOWN; } bool MayHavePrefix() const { - if (!(prefixSFlag & PREFIX_KNOWN) || !(prefixTFlag & PREFIX_KNOWN) || !(prefixDFlag & PREFIX_KNOWN)) { + if (HasUnknownPrefix()) { return true; } else if (prefixS != 0xE4 || prefixT != 0xE4 || prefixD != 0) { return true; - } else if (writeMask[0] || writeMask[1] || writeMask[2] || writeMask[3]) { + } else if (VfpuWriteMask() != 0) { return true; } return false; } + bool HasUnknownPrefix() const { + if (!(prefixSFlag & PREFIX_KNOWN) || !(prefixTFlag & PREFIX_KNOWN) || !(prefixDFlag & PREFIX_KNOWN)) { + return true; + } + return false; + } void EatPrefix() { if ((prefixSFlag & PREFIX_KNOWN) == 0 || prefixS != 0xE4) { prefixSFlag = PREFIX_KNOWN_DIRTY; @@ -98,12 +111,19 @@ struct JitState prefixTFlag = PREFIX_KNOWN_DIRTY; prefixT = 0xE4; } - if ((prefixDFlag & PREFIX_KNOWN) == 0 || prefixD != 0x0 || writeMask[0] || writeMask[1] || writeMask[2] || writeMask[3]) { + if ((prefixDFlag & PREFIX_KNOWN) == 0 || prefixD != 0x0 || VfpuWriteMask() != 0) { prefixDFlag = PREFIX_KNOWN_DIRTY; prefixD = 0x0; - writeMask[0] = writeMask[1] = writeMask[2] = writeMask[3] = false; } } + u8 VfpuWriteMask() const { + _assert_(prefixDFlag & JitState::PREFIX_KNOWN); + return (prefixD >> 8) & 0xF; + } + bool VfpuWriteMask(int i) const { + _assert_(prefixDFlag & JitState::PREFIX_KNOWN); + return (prefixD >> (8 + i)) & 1; + } }; enum CompileDelaySlotFlags @@ -122,6 +142,7 @@ class Jit : public Gen::XCodeBlock { public: Jit(MIPSState *mips); + void DoState(PointerWrap &p); // Compiled ops should ignore delay slots // the compiler will take care of them by itself @@ -150,6 +171,7 @@ class Jit : public Gen::XCodeBlock void Comp_Break(u32 op); void Comp_IType(u32 op); + void Comp_RType2(u32 op); void Comp_RType3(u32 op); void Comp_ShiftType(u32 op); void Comp_Allegrex(u32 op); @@ -164,15 +186,30 @@ class Jit : public Gen::XCodeBlock void Comp_SV(u32 op); void Comp_SVQ(u32 op); void Comp_VPFX(u32 op); + void Comp_VVectorInit(u32 op); void Comp_VDot(u32 op); void Comp_VecDo3(u32 op); + void Comp_VV2Op(u32 op); void Comp_Mftv(u32 op); void Comp_Vmtvc(u32 op); + void Comp_Vmmov(u32 op); void Comp_DoNothing(u32 op); void ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz); - void ApplyPrefixD(const u8 *vregs, u32 prefix, VectorSize sz, bool onlyWriteMask = false); + void ApplyPrefixD(const u8 *vregs, VectorSize sz); + void GetVectorRegsPrefixS(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixSFlag & JitState::PREFIX_KNOWN); + GetVectorRegs(regs, sz, vectorReg); + ApplyPrefixST(regs, js.prefixS, sz); + } + void GetVectorRegsPrefixT(u8 *regs, VectorSize sz, int vectorReg) { + _assert_(js.prefixTFlag & JitState::PREFIX_KNOWN); + GetVectorRegs(regs, sz, vectorReg); + ApplyPrefixST(regs, js.prefixT, sz); + } + void GetVectorRegsPrefixD(u8 *regs, VectorSize sz, int vectorReg); + void EatPrefix() { js.EatPrefix(); } JitBlockCache *GetBlockCache() { return &blocks; } AsmRoutineManager &Asm() { return asm_; } @@ -205,8 +242,8 @@ class Jit : public Gen::XCodeBlock // Utilities to reduce duplicated code void CompImmLogic(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompTriArith(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &), u32 (*doImm)(const u32, const u32)); - void CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)); - void CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)); + void CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg), u32 (*doImm)(const u32, const u32)); + void CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg), u32 (*doImm)(const u32, const u32)); void CompITypeMemRead(u32 op, u32 bits, void (XEmitter::*mov)(int, int, X64Reg, OpArg), void *safeFunc); void CompITypeMemWrite(u32 op, u32 bits, void *safeFunc); diff --git a/Core/MIPS/x86/RegCache.cpp b/Core/MIPS/x86/RegCache.cpp index cb863533ef19..7c36b0f37e3e 100644 --- a/Core/MIPS/x86/RegCache.cpp +++ b/Core/MIPS/x86/RegCache.cpp @@ -38,7 +38,7 @@ static const int allocationOrder[] = #endif }; -GPRRegCache::GPRRegCache() : emit(0), mips(0) { +GPRRegCache::GPRRegCache() : mips(0), emit(0) { memset(regs, 0, sizeof(regs)); memset(xregs, 0, sizeof(xregs)); } diff --git a/Core/MIPS/x86/RegCacheFPU.cpp b/Core/MIPS/x86/RegCacheFPU.cpp index fa637f7fb59f..0973863d6347 100644 --- a/Core/MIPS/x86/RegCacheFPU.cpp +++ b/Core/MIPS/x86/RegCacheFPU.cpp @@ -21,7 +21,9 @@ #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/x86/RegCacheFPU.h" -FPURegCache::FPURegCache() : emit(0), mips(0) { +u32 FPURegCache::tempValues[NUM_TEMPS]; + +FPURegCache::FPURegCache() : mips(0), emit(0) { memset(regs, 0, sizeof(regs)); memset(xregs, 0, sizeof(xregs)); vregs = regs + 32; @@ -37,6 +39,7 @@ void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) { regs[i].location = GetDefaultLocation(i); regs[i].away = false; regs[i].locked = false; + regs[i].tempLocked = false; } } @@ -79,6 +82,11 @@ void FPURegCache::MapRegsV(const u8 *v, VectorSize sz, int flags) { } } +void FPURegCache::ReleaseSpillLock(int mipsreg) +{ + regs[mipsreg].locked = false; +} + void FPURegCache::ReleaseSpillLocks() { for (int i = 0; i < NUM_MIPS_FPRS; i++) regs[i].locked = false; @@ -99,9 +107,7 @@ void FPURegCache::BindToRegister(const int i, bool doLoad, bool makeDirty) { if (!regs[i].location.IsImm() && (regs[i].location.offset & 0x3)) { PanicAlert("WARNING - misaligned fp register location %i", i); } - if (i < TEMP0) { - emit->MOVSS(xr, regs[i].location); - } + emit->MOVSS(xr, regs[i].location); } regs[i].location = newloc; regs[i].away = true; @@ -138,17 +144,31 @@ void FPURegCache::DiscardR(int i) { xregs[xr].mipsReg = -1; regs[i].location = GetDefaultLocation(i); regs[i].away = false; + regs[i].tempLocked = false; } else { // _assert_msg_(DYNA_REC,0,"already stored"); + regs[i].tempLocked = false; } } -bool FPURegCache::IsTemp(X64Reg xr) { +bool FPURegCache::IsTempX(X64Reg xr) { return xregs[xr].mipsReg >= TEMP0; } +int FPURegCache::GetTempR() { + for (int r = TEMP0; r < TEMP0 + NUM_TEMPS; ++r) { + if (!regs[r].away && !regs[r].tempLocked) { + regs[r].tempLocked = true; + return r; + } + } + + _assert_msg_(DYNA_REC, 0, "Regcache ran out of temp regs, might need to DiscardR() some."); + return -1; +} + void FPURegCache::Flush() { - for (int i = 0; i < TEMP0; i++) { + for (int i = 0; i < NUM_MIPS_FPRS; i++) { if (regs[i].locked) { PanicAlert("Somebody forgot to unlock MIPS reg %i.", i); } @@ -164,16 +184,15 @@ void FPURegCache::Flush() { } } } - for (int i = TEMP0; i < TEMP0 + NUM_TEMPS; ++i) { - DiscardR(i); - } } OpArg FPURegCache::GetDefaultLocation(int reg) const { if (reg < 32) { return M(&mips->f[reg]); - } else { + } else if (reg < 32 + 128) { return M(&mips->v[reg - 32]); + } else { + return M(&tempValues[reg - 32 - 128]); } } diff --git a/Core/MIPS/x86/RegCacheFPU.h b/Core/MIPS/x86/RegCacheFPU.h index af5d2f495323..37d7c4c2e6a4 100644 --- a/Core/MIPS/x86/RegCacheFPU.h +++ b/Core/MIPS/x86/RegCacheFPU.h @@ -27,14 +27,14 @@ using namespace Gen; // GPRs are numbered 0 to 31 // VFPU regs are numbered 32 to 159. -// Then we have some temp regs for VFPU handling from 160 to 167. +// Then we have some temp regs for VFPU handling from 160 to 175. + +// Temp regs: 4 from S prefix, 4 from T prefix, 4 from D mask, and 4 for work (worst case.) +// But most of the time prefixes aren't used that heavily so we won't use all of them. enum { - NUM_TEMPS = 4, + NUM_TEMPS = 16, TEMP0 = 32 + 128, - TEMP1 = TEMP0 + 1, - TEMP2 = TEMP0 + 2, - TEMP3 = TEMP0 + 3, NUM_MIPS_FPRS = 32 + 128 + NUM_TEMPS, }; @@ -53,6 +53,8 @@ struct MIPSCachedFPReg { OpArg location; bool away; // value not in source register bool locked; + // Only for temp regs. + bool tempLocked; }; enum { @@ -80,7 +82,11 @@ class FPURegCache void DiscardV(int vreg) { DiscardR(vreg + 32); } - bool IsTemp(X64Reg xreg); + bool IsTempX(X64Reg xreg); + int GetTempR(); + int GetTempV() { + return GetTempR() - 32; + } void SetEmitter(XEmitter *emitter) {emit = emitter;} @@ -108,6 +114,7 @@ class FPURegCache // Register locking. Prevents them from being spilled. void SpillLock(int p1, int p2=0xff, int p3=0xff, int p4=0xff); + void ReleaseSpillLock(int mipsrega); void ReleaseSpillLocks(); void MapRegV(int vreg, int flags); @@ -118,6 +125,9 @@ class FPURegCache } void SpillLockV(const u8 *v, VectorSize vsz); void SpillLockV(int vec, VectorSize vsz); + void ReleaseSpillLockV(int vreg) { + ReleaseSpillLock(vreg + 32); + } MIPSState *mips; @@ -130,5 +140,8 @@ class FPURegCache X64CachedFPReg xregs[NUM_X_FPREGS]; MIPSCachedFPReg *vregs; + // TEMP0, etc. are swapped in here if necessary (e.g. on x86.) + static u32 tempValues[NUM_TEMPS]; + XEmitter *emit; }; diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index d174cdb7e144..dcc895d212b1 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -56,6 +56,10 @@ u8 *GetPointer(const u32 address) else { ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); + if (!g_Config.bIgnoreBadMemAccess) { + Core_EnableStepping(true); + host->SetDebugMode(true); + } return 0; } } diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index e73ec60564d8..2e582d22d145 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -89,6 +89,33 @@ bool Load_PSP_ISO(const char *filename, std::string *error_string) if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT.OLD").exists) { bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.OLD"; } + // bypass another patchers + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT.DAT").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.DAT"; + } + // bypass more patchers + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT.BI").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.BI"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT.LLD").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.LLD"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/OLD_EBOOT.BIN").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/OLD_EBOOT.BIN"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT.123").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.123"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/EBOOT_LRC_CH.BIN").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT_LRC_CH.BIN"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/BOOT0.OLD").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/BOOT0.OLD"; + } + if (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/SYSDIR/BOOT1.OLD").exists) { + bootpath = "disc0:/PSP_GAME/SYSDIR/BOOT1.OLD"; + } + bool hasEncrypted = false; u32 fd; if ((fd = pspFileSystem.OpenFile(bootpath, FILEACCESS_READ)) != 0) diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp new file mode 100644 index 000000000000..a64528c61e5c --- /dev/null +++ b/Core/Reporting.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/StdThread.h" +#include "Core/Config.h" +#include "Core/System.h" + +#include "net/http_client.h" +#include "net/resolve.h" +#include "base/buffer.h" + +#include +#include +#include + +namespace Reporting +{ + const int DEFAULT_PORT = 80; + const u32 SPAM_LIMIT = 100; + const int PAYLOAD_BUFFER_SIZE = 100; + + // Internal limiter on number of requests per instance. + static u32 spamProtectionCount = 0; + // Temporarily stores a reference to the hostname. + static std::string lastHostname; + + enum RequestType + { + MESSAGE, + }; + + struct Payload + { + RequestType type; + std::string string1; + std::string string2; + }; + static Payload payloadBuffer[PAYLOAD_BUFFER_SIZE]; + static int payloadBufferPos = 0; + + static size_t ServerHostnameLength() + { + if (g_Config.sReportHost.empty()) + return g_Config.sReportHost.npos; + + // IPv6 literal? + if (g_Config.sReportHost[0] == '[') + { + size_t length = g_Config.sReportHost.find("]:"); + if (length != g_Config.sReportHost.npos) + ++length; + return length; + } + else + return g_Config.sReportHost.find(':'); + } + + static const char *ServerHostname() + { + if (g_Config.sReportHost.empty()) + return NULL; + // Disabled by default for now. + if (g_Config.sReportHost.compare("default") == 0) + return NULL; + + size_t length = ServerHostnameLength(); + if (length == g_Config.sReportHost.npos) + return g_Config.sReportHost.c_str(); + + lastHostname = g_Config.sReportHost.substr(0, length); + return lastHostname.c_str(); + } + + static int ServerPort() + { + if (g_Config.sReportHost.empty()) + return 0; + // Disabled by default for now. + if (g_Config.sReportHost.compare("default") == 0) + return 0; + + size_t offset = ServerHostnameLength(); + if (offset == g_Config.sReportHost.npos) + return DEFAULT_PORT; + + // Skip the colon. + std::string port = g_Config.sReportHost.substr(offset + 1); + return atoi(port.c_str()); + } + + // Should only be called once per request. + bool CheckSpamLimited() + { + return ++spamProtectionCount >= SPAM_LIMIT; + } + + bool SendReportRequest(const char *uri, const std::string &data, Buffer *output = NULL) + { + bool result = false; + http::Client http; + Buffer theVoid; + + if (output == NULL) + output = &theVoid; + + net::Init(); + if (http.Resolve(ServerHostname(), ServerPort())) + { + http.Connect(); + http.POST("/report/message", data, "application/x-www-urlencoded", output); + http.Disconnect(); + result = true; + } + net::Shutdown(); + + return result; + } + + int Process(int pos) + { + Payload &payload = payloadBuffer[pos]; + + const int PARAM_BUFFER_SIZE = 4096; + char temp[PARAM_BUFFER_SIZE]; + + // TODO: Need to escape these values, add more. + snprintf(temp, PARAM_BUFFER_SIZE - 1, "version=%s&game=%s_%s", + PPSSPP_GIT_VERSION, + g_paramSFO.GetValueString("DISC_ID").c_str(), + g_paramSFO.GetValueString("DISC_VERSION").c_str()); + + std::string data; + switch (payload.type) + { + case MESSAGE: + // TODO: Escape. + data = std::string(temp) + "&message=" + payload.string1 + "&value=" + payload.string2; + payload.string1.clear(); + payload.string2.clear(); + + SendReportRequest("/report/message", data); + break; + } + + return 0; + } + + void ReportMessage(const char *message, ...) + { + if (g_Config.sReportHost.empty() || CheckSpamLimited()) + return; + // Disabled by default for now. + if (g_Config.sReportHost.compare("default") == 0) + return; + + const int MESSAGE_BUFFER_SIZE = 32768; + char temp[MESSAGE_BUFFER_SIZE]; + + va_list args; + va_start(args, message); + vsnprintf(temp, MESSAGE_BUFFER_SIZE - 1, message, args); + temp[MESSAGE_BUFFER_SIZE - 1] = '\0'; + va_end(args); + + int pos = payloadBufferPos++ % PAYLOAD_BUFFER_SIZE; + Payload &payload = payloadBuffer[pos]; + payload.type = MESSAGE; + payload.string1 = message; + payload.string2 = temp; + + std::thread th(Process, pos); + th.detach(); + } + +} \ No newline at end of file diff --git a/Core/Reporting.h b/Core/Reporting.h new file mode 100644 index 000000000000..52de51a38572 --- /dev/null +++ b/Core/Reporting.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/CommonTypes.h" + +namespace Reporting +{ + void ReportMessage(const char *message, ...); +} \ No newline at end of file diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 8a450081c34f..a320c4e70b63 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -90,7 +90,7 @@ namespace SaveState // Don't actually run it until next CoreTiming::Advance(). // It's possible there might be a duplicate but it won't hurt us. - if (Core_IsStepping() && __KernelIsRunning()) + if (Core_IsInactive() && __KernelIsRunning()) { // Warning: this may run on a different thread. Process(0, 0); diff --git a/Core/System.cpp b/Core/System.cpp index 11fa6032a622..8a0581b7bf0e 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -32,6 +32,7 @@ #include "HLE/sceKernel.h" #include "HLE/sceKernelMemory.h" #include "HLE/sceAudio.h" +#include "Config.h" #include "Core.h" #include "CoreTiming.h" #include "CoreParameter.h" @@ -47,6 +48,8 @@ static PSPMixer *mixer; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { + INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION); + coreParameter = coreParam; currentCPU = &mipsr4k; numCPUs = 1; diff --git a/Core/Util/BlockAllocator.cpp b/Core/Util/BlockAllocator.cpp index 227d28f1abeb..24809fe1be76 100644 --- a/Core/Util/BlockAllocator.cpp +++ b/Core/Util/BlockAllocator.cpp @@ -1,3 +1,20 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + #include "Log.h" #include "BlockAllocator.h" #include "ChunkFile.h" @@ -28,7 +45,7 @@ void BlockAllocator::Shutdown() blocks.clear(); } -u32 BlockAllocator::AllocAligned(u32 &size, u32 grain, bool fromTop, const char *tag) +u32 BlockAllocator::AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromTop, const char *tag) { // Sanity check if (size == 0 || size > rangeSize_) { @@ -39,17 +56,21 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 grain, bool fromTop, const char // It could be off step, but the grain should generally be a power of 2. if (grain < grain_) grain = grain_; + if (sizeGrain < grain_) + sizeGrain = grain_; // upalign size to grain - size = (size + grain - 1) & ~(grain - 1); + size = (size + sizeGrain - 1) & ~(sizeGrain - 1); if (!fromTop) { //Allocate from bottom of mem for (std::list::iterator iter = blocks.begin(); iter != blocks.end(); iter++) { - BlockAllocator::Block &b = *iter; + Block &b = *iter; u32 offset = b.start % grain; + if (offset != 0) + offset = grain - offset; u32 needed = offset + size; if (b.taken == false && b.size >= needed) { @@ -75,9 +96,8 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 grain, bool fromTop, const char // Allocate from top of mem. for (std::list::reverse_iterator iter = blocks.rbegin(); iter != blocks.rend(); ++iter) { - std::list::reverse_iterator hey = iter; - BlockAllocator::Block &b = *((++hey).base()); //yes, confusing syntax. reverse_iterators are confusing - u32 offset = b.start % grain; + Block &b = *iter; + u32 offset = (b.start + b.size - size) % grain; u32 needed = offset + size; if (b.taken == false && b.size >= needed) { @@ -85,16 +105,17 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 grain, bool fromTop, const char { b.taken = true; b.SetTag(tag); - return b.start + offset; + return b.start; } else { - blocks.insert(hey.base(), Block(b.start, b.size - needed, false)); + std::list::iterator pos = iter.base(); + blocks.insert(--pos, Block(b.start, b.size - needed, false)); b.taken = true; b.start += b.size - needed; b.size = needed; b.SetTag(tag); - return b.start + offset; + return b.start; } } } @@ -109,7 +130,7 @@ u32 BlockAllocator::AllocAligned(u32 &size, u32 grain, bool fromTop, const char u32 BlockAllocator::Alloc(u32 &size, bool fromTop, const char *tag) { // We want to make sure it's aligned in case AllocAt() was used. - return AllocAligned(size, grain_, fromTop, tag); + return AllocAligned(size, grain_, grain_, fromTop, tag); } u32 BlockAllocator::AllocAt(u32 position, u32 size, const char *tag) diff --git a/Core/Util/BlockAllocator.h b/Core/Util/BlockAllocator.h index 98cb3e1cd2cd..a4354b6dbb9a 100644 --- a/Core/Util/BlockAllocator.h +++ b/Core/Util/BlockAllocator.h @@ -1,3 +1,19 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once @@ -9,8 +25,7 @@ class PointerWrap; -// Generic allocator thingy -// Allocates blocks from a range +// Generic allocator thingy. Allocates blocks from a range. class BlockAllocator { @@ -25,7 +40,7 @@ class BlockAllocator // WARNING: size can be modified upwards! u32 Alloc(u32 &size, bool fromTop = false, const char *tag = 0); - u32 AllocAligned(u32 &size, u32 grain, bool fromTop = false, const char *tag = 0); + u32 AllocAligned(u32 &size, u32 sizeGrain, u32 grain, bool fromTop = false, const char *tag = 0); u32 AllocAt(u32 position, u32 size, const char *tag = 0); bool Free(u32 position); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 4a77375ac212..c920d73018cb 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -100,7 +100,7 @@ static void EndVertexDataAndDraw(int prim) { static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) { u32 ptr = kernelMemory.Alloc(size, fromTop, name); // Didn't get it. - if (ptr == -1) + if (ptr == (u32)-1) return 0; return ptr; } @@ -199,15 +199,19 @@ void PPGeBegin() dataWritePtr = dataPtr; // Set up the correct states for UI drawing + WriteCmd(GE_CMD_OFFSETADDR, 0); WriteCmd(GE_CMD_ALPHABLENDENABLE, 1); WriteCmd(GE_CMD_BLENDMODE, 2 | (3 << 4)); WriteCmd(GE_CMD_ALPHATESTENABLE, 0); WriteCmd(GE_CMD_COLORTESTENABLE, 0); WriteCmd(GE_CMD_ZTESTENABLE, 0); + WriteCmd(GE_CMD_LIGHTINGENABLE, 0); WriteCmd(GE_CMD_FOGENABLE, 0); WriteCmd(GE_CMD_STENCILTESTENABLE, 0); WriteCmd(GE_CMD_CULLFACEENABLE, 0); WriteCmd(GE_CMD_CLEARMODE, 0); // Normal mode + WriteCmd(GE_CMD_MASKRGB, 0); + WriteCmd(GE_CMD_MASKALPHA, 0); PPGeSetDefaultTexture(); diff --git a/Core/Util/Pool.h b/Core/Util/Pool.h deleted file mode 100644 index dee8633c103b..000000000000 --- a/Core/Util/Pool.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "../../Globals.h" - -// pool allocator -template -class Pool -{ - T pool[size]; - int count; -public: - Pool() - { - Reset(); - } - void Reset() - { - count=0; - } - T* Alloc() - { - _dbg_assert_msg_(CPU,countdirtyAfterDisplay; + } + return true; +} + void GLES_GPU::CopyDisplayToOutput() { + glstate.colorMask.set(true, true, true, true); transformDraw_.Flush(); - if (!g_Config.bBufferedRendering) - return; EndDebugDraw(); framebufferManager_.CopyDisplayToOutput(); + framebufferManager_.EndFrame(); shaderManager_->DirtyShader(); shaderManager_->DirtyUniform(DIRTY_ALL); @@ -280,32 +291,12 @@ void GLES_GPU::Break() { } -static void EnterClearMode(u32 data) { - bool colMask = (data >> 8) & 1; - bool alphaMask = (data >> 9) & 1; - bool updateZ = (data >> 10) & 1; - glstate.colorMask.set(colMask, colMask, colMask, alphaMask); - glstate.depthWrite.set(updateZ ? GL_TRUE : GL_FALSE); -} - -static void LeaveClearMode() { - // We have to reset the following state as per the state of the command registers: - // Back face culling - // Texture map enable (meh) - // Fogging - // Antialiasing - // Alpha test - glstate.colorMask.set(1,1,1,1); - glstate.depthWrite.set(!(gstate.zmsk & 1) ? GL_TRUE : GL_FALSE); - // dirtyshader? -} - void GLES_GPU::PreExecuteOp(u32 op, u32 diff) { u32 cmd = op >> 24; if (flushBeforeCommand_[cmd] == 1 || (diff && flushBeforeCommand_[cmd] == 2)) { if (dumpThisFrame_) { - NOTICE_LOG(G3D, "================ FLUSH ================"); + NOTICE_LOG(HLE, "================ FLUSH ================"); } transformDraw_.Flush(); } @@ -330,7 +321,14 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_PRIM: { + // This drives all drawing. All other state we just buffer up, then we apply it only + // when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization. + + // This also make skipping drawing very effective. + framebufferManager_.SetRenderFrameBuffer(); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) + return; u32 count = data & 0xFFFF; u32 type = data >> 16; @@ -539,6 +537,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_CULLFACEENABLE: + case GE_CMD_CULL: break; case GE_CMD_TEXTUREMAPENABLE: @@ -639,6 +638,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_CLUTADDR: case GE_CMD_CLUTADDRUPPER: case GE_CMD_LOADCLUT: + case GE_CMD_CLUTFORMAT: gstate_c.textureChanged = true; // This could be used to "dirty" textures with clut. break; @@ -647,10 +647,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_TEXSHADELS: break; - case GE_CMD_CLUTFORMAT: - gstate_c.textureChanged = true; - break; - case GE_CMD_TRANSFERSRC: case GE_CMD_TRANSFERSRCW: case GE_CMD_TRANSFERDST: @@ -666,6 +662,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { { // TODO: Here we should check if the transfer overlaps a framebuffer or any textures, // and take appropriate action. This is a block transfer between RAM and VRAM, or vice versa. + // Can we skip this on SkipDraw? DoBlockTransfer(); break; } @@ -804,9 +801,6 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_LIGHTENABLE3: break; - case GE_CMD_CULL: - break; - case GE_CMD_SHADEMODE: break; @@ -823,14 +817,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { // CLEARING ////////////////////////////////////////////////////////////////// case GE_CMD_CLEARMODE: - // If it becomes a performance problem, check diff&1 - if (data & 1) - EnterClearMode(data); - else - LeaveClearMode(); break; - ////////////////////////////////////////////////////////////////// // ALPHA BLENDING ////////////////////////////////////////////////////////////////// @@ -842,7 +830,12 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_ALPHATESTENABLE: case GE_CMD_COLORTESTENABLE: - // This is done in the shader. + // They are done in the fragment shader. + break; + + case GE_CMD_COLORTEST: + case GE_CMD_COLORTESTMASK: + shaderManager_->DirtyUniform(DIRTY_COLORMASK); break; case GE_CMD_COLORREF: @@ -989,8 +982,8 @@ void GLES_GPU::UpdateStats() { gpuStats.numVertexShaders = shaderManager_->NumVertexShaders(); gpuStats.numFragmentShaders = shaderManager_->NumFragmentShaders(); gpuStats.numShaders = shaderManager_->NumPrograms(); - gpuStats.numTextures = textureCache_.NumLoadedTextures(); - gpuStats.numFBOs = framebufferManager_.NumVFBs(); + gpuStats.numTextures = (int)textureCache_.NumLoadedTextures(); + gpuStats.numFBOs = (int)framebufferManager_.NumVFBs(); } void GLES_GPU::DoBlockTransfer() { diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index 52e1883730f8..9b30cab7b59e 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -63,7 +63,7 @@ class GLES_GPU : public GPUCommon { return textureCache_.DecodeTexture(dest, state); } - + virtual bool FramebufferDirty(); std::vector GetFramebufferList(); diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index b0e8fc6a7ca0..321ae0b0c066 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -88,12 +88,14 @@ void GenerateFragmentShader(char *buffer) bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear(); bool enableAlphaTest = (gstate.alphaTestEnable & 1) && !gstate.isModeClear(); bool enableColorTest = (gstate.colorTestEnable & 1) && !gstate.isModeClear(); + bool enableColorDoubling = (gstate.texfunc & 0x10000) != 0; if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); if (enableAlphaTest || enableColorTest) { WRITE(p, "uniform vec4 u_alphacolorref;\n"); + WRITE(p, "uniform vec4 u_colormask;\n"); } if (gstate.textureMapEnable & 1) { WRITE(p, "uniform vec3 u_texenv;\n"); @@ -169,8 +171,8 @@ void GenerateFragmentShader(char *buffer) // No texture mapping WRITE(p, " vec4 v = v_color0 %s;\n", secondary); } - // Color doubling - if (gstate.texfunc & 0x10000) { + + if (enableColorDoubling) { WRITE(p, " v = v * 2.0;\n"); } @@ -189,8 +191,8 @@ void GenerateFragmentShader(char *buffer) const char *colorTestFuncs[] = { "#", "#", " == ", " != " }; // never/always don't make sense} int colorTestMask = gstate.colormask; if (colorTestFuncs[colorTestFunc][0] != '#') - WRITE(p, "if (!(v.rgb %s u_alphacolorref.rgb)) discard;\n", colorTestFuncs[colorTestFunc]); - }*/ + WRITE(p, "if (!(v.rgb %s (u_alphacolorref.rgb & u_colormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]); + }*/ if (enableFog) { WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n"); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index bc513f90e07d..d2f7e931dd1b 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -125,7 +125,9 @@ FramebufferManager::FramebufferManager() : // And an initial clear. We don't clear per frame as the games are supposed to handle that // by themselves. - glClearColor(0, 0, 0, 0); + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); convBuf = new u8[480 * 272 * 4]; @@ -235,7 +237,7 @@ void FramebufferManager::DrawActiveTexture(float x, float y, float w, float h, b glsl_unbind(); } -FramebufferManager::VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { +VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; if (MaskedEqual(v->fb_address, displayFramebufPtr_) && v->format == displayFormat_) { @@ -264,8 +266,6 @@ void GetViewportDimensions(int *w, int *h) { } void FramebufferManager::SetRenderFrameBuffer() { - if (!g_Config.bBufferedRendering) - return; // Get parameters u32 fb_address = (gstate.fbptr & 0xFFE000) | ((gstate.fbwidth & 0xFF0000) << 8); int fb_stride = gstate.fbwidth & 0x3C0; @@ -325,60 +325,105 @@ void FramebufferManager::SetRenderFrameBuffer() { vfb->renderWidth = (u16)(drawing_width * renderWidthFactor); vfb->renderHeight = (u16)(drawing_height * renderHeightFactor); vfb->format = fmt; + vfb->usageFlags = FB_USAGE_RENDERTARGET; + vfb->dirtyAfterDisplay = true; - vfb->colorDepth = FBO_8888; switch (fmt) { - case GE_FORMAT_4444: vfb->colorDepth = FBO_4444; - case GE_FORMAT_5551: vfb->colorDepth = FBO_5551; - case GE_FORMAT_565: vfb->colorDepth = FBO_565; - case GE_FORMAT_8888: vfb->colorDepth = FBO_8888; + case GE_FORMAT_4444: vfb->colorDepth = FBO_4444; break; + case GE_FORMAT_5551: vfb->colorDepth = FBO_5551; break; + case GE_FORMAT_565: vfb->colorDepth = FBO_565; break; + case GE_FORMAT_8888: vfb->colorDepth = FBO_8888; break; + default: vfb->colorDepth = FBO_8888; break; } + + if (g_Config.bTrueColor) + vfb->colorDepth = FBO_8888; + //#ifdef ANDROID // vfb->colorDepth = FBO_8888; //#endif - vfb->fbo = fbo_create(vfb->renderWidth, vfb->renderHeight, 1, true, vfb->colorDepth); - textureCache_->NotifyFramebuffer(vfb->fb_address, vfb->fbo); + if (g_Config.bBufferedRendering) + { + vfb->fbo = fbo_create(vfb->renderWidth, vfb->renderHeight, 1, true, vfb->colorDepth); + } + else + vfb->fbo = 0; + + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb); vfb->last_frame_used = gpuStats.numFrames; vfbs_.push_back(vfb); - fbo_bind_as_render_target(vfb->fbo); + if (g_Config.bBufferedRendering) { + fbo_bind_as_render_target(vfb->fbo); + } else { + fbo_unbind(); + // Let's ignore rendering to targets that have not (yet) been displayed. + gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB; + } + glEnable(GL_DITHER); glstate.viewport.set(0, 0, vfb->renderWidth, vfb->renderHeight); currentRenderVfb_ = vfb; + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); INFO_LOG(HLE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); - return; - } - if (vfb != currentRenderVfb_) { + // We already have it! + } else if (vfb != currentRenderVfb_) { // Use it as a render target. DEBUG_LOG(HLE, "Switching render target to FBO for %08x", vfb->fb_address); + vfb->usageFlags |= FB_USAGE_RENDERTARGET; gstate_c.textureChanged = true; if (vfb->last_frame_used != gpuStats.numFrames) { // Android optimization //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } vfb->last_frame_used = gpuStats.numFrames; - - fbo_bind_as_render_target(vfb->fbo); + vfb->dirtyAfterDisplay = true; + + if (g_Config.bBufferedRendering && vfb->fbo) { + fbo_bind_as_render_target(vfb->fbo); + } else { + fbo_unbind(); + + // Let's ignore rendering to targets that have not (yet) been displayed. + if (vfb->usageFlags & FB_USAGE_DISPLAYED_FRAMEBUFFER) + gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB; + else + gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB; + + /* + if (drawing_width == 480 && drawing_height == 272) { + gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPNONFB; + // OK! + } else { + gstate_c.skipDrawReason |= ~SKIPDRAW_SKIPNONFB; + }*/ + } + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb); - textureCache_->NotifyFramebuffer(vfb->fb_address, vfb->fbo); #ifdef USING_GLES2 // Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering // to it. This broke stuff before, so now it only clears on the first use of an // FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs // performance-crushing framebuffer reloads from RAM, but we'll have to live with that. if (vfb->last_frame_used != gpuStats.numFrames) + { + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } #endif glstate.viewport.set(0, 0, vfb->renderWidth, vfb->renderHeight); currentRenderVfb_ = vfb; } } - void FramebufferManager::CopyDisplayToOutput() { fbo_unbind(); @@ -386,36 +431,48 @@ void FramebufferManager::CopyDisplayToOutput() { if (!vfb) { DEBUG_LOG(HLE, "Found no FBO! displayFBPtr = %08x", displayFramebufPtr_); // No framebuffer to display! Clear to black. + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); return; } + vfb->usageFlags |= FB_USAGE_DISPLAYED_FRAMEBUFFER; + vfb->dirtyAfterDisplay = false; + prevPrevDisplayFramebuf_ = prevDisplayFramebuf_; prevDisplayFramebuf_ = displayFramebuf_; displayFramebuf_ = vfb; - glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); - currentRenderVfb_ = 0; - DEBUG_LOG(HLE, "Displaying FBO %08x", vfb->fb_address); - glstate.blend.disable(); - glstate.cullFace.disable(); - glstate.depthTest.disable(); - glstate.scissorTest.disable(); + if (vfb->fbo) { + glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); + DEBUG_LOG(HLE, "Displaying FBO %08x", vfb->fb_address); + glstate.blend.disable(); + glstate.cullFace.disable(); + glstate.depthTest.disable(); + glstate.scissorTest.disable(); + glstate.stencilTest.disable(); - fbo_bind_color_as_texture(vfb->fbo, 0); + fbo_bind_color_as_texture(vfb->fbo, 0); + + // These are in the output display coordinates + float x, y, w, h; + CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight); + DrawActiveTexture(x, y, w, h, true); + } if (resized_) { + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } - // These are in the output display coordinates - float x, y, w, h; - CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight); - DrawActiveTexture(x, y, w, h, true); +} +void FramebufferManager::EndFrame() { if (resized_) { DestroyAllFBOs(); glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight); @@ -432,6 +489,8 @@ void FramebufferManager::BeginFrame() { glstate.cullFace.disable(); glstate.depthTest.disable(); glstate.blend.disable(); + glstate.scissorTest.disable(); + glstate.stencilTest.disable(); DrawPixels(pspframebuf, displayFormat_, displayStride_); // TODO: restore state? } @@ -449,8 +508,7 @@ void FramebufferManager::SetDisplayFramebuffer(u32 framebuf, u32 stride, int for } } -std::vector FramebufferManager::GetFramebufferList() -{ +std::vector FramebufferManager::GetFramebufferList() { std::vector list; for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { @@ -478,8 +536,11 @@ void FramebufferManager::DecimateFBOs() { } if ((*iter)->last_frame_used + FBO_OLD_AGE < gpuStats.numFrames) { INFO_LOG(HLE, "Destroying FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->format) - textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb->fbo); - fbo_destroy(vfb->fbo); + if (vfb->fbo) { + textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); + fbo_destroy(vfb->fbo); + vfb->fbo = 0; + } delete vfb; vfbs_.erase(iter++); } @@ -491,8 +552,9 @@ void FramebufferManager::DecimateFBOs() { void FramebufferManager::DestroyAllFBOs() { for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *vfb = *iter; - textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb->fbo); - fbo_destroy(vfb->fbo); + textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); + if (vfb->fbo) + fbo_destroy(vfb->fbo); delete vfb; } vfbs_.clear(); diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index c5a86426c9ee..842294373fe2 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -38,6 +38,37 @@ enum PspDisplayPixelFormat { PSP_DISPLAY_PIXEL_FORMAT_8888 = 3, }; +enum { + FB_USAGE_DISPLAYED_FRAMEBUFFER = 1, + FB_USAGE_RENDERTARGET = 2, + FB_USAGE_TEXTURE = 4, +}; + + +struct VirtualFramebuffer { + int last_frame_used; + + u32 fb_address; + u32 z_address; + int fb_stride; + int z_stride; + + // There's also a top left of the drawing region, but meh... + u16 width; + u16 height; + u16 renderWidth; + u16 renderHeight; + + u16 usageFlags; + + int format; // virtual, right now they are all RGBA8888 + FBOColorDepth colorDepth; + FBO *fbo; + + bool dirtyAfterDisplay; +}; + + class FramebufferManager { public: FramebufferManager(); @@ -47,25 +78,6 @@ class FramebufferManager { textureCache_ = tc; } - struct VirtualFramebuffer { - int last_frame_used; - - u32 fb_address; - u32 z_address; - int fb_stride; - int z_stride; - - // There's also a top left of the drawing region, but meh... - u16 width; - u16 height; - u16 renderWidth; - u16 renderHeight; - - int format; // virtual, right now they are all RGBA8888 - FBOColorDepth colorDepth; - FBO *fbo; - }; - void DrawPixels(const u8 *framebuf, int pixelFormat, int linesize); void DrawActiveTexture(float x, float y, float w, float h, bool flip = false); @@ -73,6 +85,7 @@ class FramebufferManager { void DecimateFBOs(); void BeginFrame(); + void EndFrame(); void Resized(); void CopyDisplayToOutput(); void SetRenderFrameBuffer(); // Uses parameters computed from gstate diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 6a985c844623..4675f960f831 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -89,6 +89,7 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs) u_fogcolor = glGetUniformLocation(program, "u_fogcolor"); u_fogcoef = glGetUniformLocation(program, "u_fogcoef"); u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref"); + u_colormask = glGetUniformLocation(program, "u_colormask"); // Transform u_view = glGetUniformLocation(program, "u_view"); @@ -243,7 +244,7 @@ void LinkedShader::updateUniforms() { if (u_proj_through != -1 && (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX)) { Matrix4x4 proj_through; - proj_through.setOrtho(0.0f, 480, 272, 0, -1, 0); // TODO: Store this somewhere instead of regenerating! And not in each LinkedShader object! + proj_through.setOrtho(0.0f, 480, 272, 0, 0, 1); glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr()); } if (u_texenv != -1 && (dirtyUniforms & DIRTY_TEXENV)) { @@ -252,6 +253,9 @@ void LinkedShader::updateUniforms() { if (u_alphacolorref != -1 && (dirtyUniforms & DIRTY_ALPHACOLORREF)) { SetColorUniform3Alpha(u_alphacolorref, gstate.colorref, (gstate.alphatest >> 8) & 0xFF); } + if (u_colormask != -1 && (dirtyUniforms & DIRTY_COLORMASK)) { + SetColorUniform3(u_colormask, gstate.colormask); + } if (u_fogcolor != -1 && (dirtyUniforms & DIRTY_FOGCOLOR)) { SetColorUniform3(u_fogcolor, gstate.fogcolor); } @@ -372,8 +376,7 @@ void ShaderManager::DirtyShader() } -LinkedShader *ShaderManager::ApplyShader(int prim) -{ +LinkedShader *ShaderManager::ApplyShader(int prim) { if (globalDirty) { if (lastShader) lastShader->dirtyUniforms |= globalDirty; diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index c83ad3a66ac4..38bb73e80563 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -58,6 +58,7 @@ class LinkedShader // Fragment processing inputs int u_alphacolorref; + int u_colormask; int u_fogcolor; int u_fogcoef; @@ -88,7 +89,7 @@ enum DIRTY_TEXENV = (1 << 4), DIRTY_ALPHACOLORREF = (1 << 5), DIRTY_COLORREF = (1 << 6), - + DIRTY_COLORMASK = (1 << 7), DIRTY_LIGHT0 = (1 << 8), DIRTY_LIGHT1 = (1 << 9), DIRTY_LIGHT2 = (1 << 10), diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 9d9deca2571f..1b377de734c0 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -84,22 +84,19 @@ void TransformDrawEngine::ApplyDrawState(int prim) { gstate_c.textureChanged = false; } - // TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a - // single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily. - // Set cull bool wantCull = !gstate.isModeClear() && !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled(); glstate.cullFace.set(wantCull); + if (wantCull) + glstate.cullFaceMode.set(cullingMode[gstate.getCullMode()]); - if (wantCull) { - u8 cullMode = gstate.getCullMode(); - glstate.cullFaceMode.set(cullingMode[cullMode]); - } + // TODO: The top bit of the alpha channel should be written to the stencil bit somehow. This appears to require very expensive multipass rendering :( Alternatively, one could do a + // single fullscreen pass that converts alpha to stencil (or 2 passes, to set both the 0 and 1 values) very easily. // Set blend - bool wantBlend = !gstate.isModeClear() && (gstate.alphaBlendEnable & 1); + bool wantBlend = !gstate.isModeClear() && gstate.isAlphaBlendEnabled(); glstate.blend.set(wantBlend); - if(wantBlend) { + if (wantBlend) { // This can't be done exactly as there are several PSP blend modes that are impossible to do on OpenGL ES 2.0, and some even on regular OpenGL for desktop. // HOWEVER - we should be able to approximate the 2x modes in the shader, although they will clip wrongly. @@ -168,46 +165,54 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } } - bool wantDepthTest = gstate.isModeClear() || gstate.isDepthTestEnabled(); - glstate.depthTest.set(wantDepthTest); - if(wantDepthTest) { - // Force GL_ALWAYS if mode clear - without depth test, no depth write. - int depthTestFunc = gstate.isModeClear() ? 1 : gstate.getDepthTestFunc(); - glstate.depthFunc.set(ztests[depthTestFunc]); - } + // Set Dither + glstate.dither.set(gstate.isDitherEnabled()); + + // Set ColorMask/Stencil/Depth + if (gstate.isModeClear()) { + bool colorMask = (gstate.clearmode >> 8) & 1; + bool alphaMask = (gstate.clearmode >> 9) & 1; + bool depthMask = ((gstate.clearmode >> 10) & 1) || !(gstate.zmsk & 1); + + glstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask); + + glstate.stencilTest.enable(); + glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glstate.stencilFunc.set(GL_ALWAYS, 0, 0xFF); + + glstate.depthTest.enable(); + glstate.depthFunc.set(GL_ALWAYS); + glstate.depthWrite.set(depthMask ? GL_TRUE : GL_FALSE); - // PSP color/alpha mask is per bit but we can only support per byte. - // But let's do that, at least. And let's try a threshold. - if (!gstate.isModeClear()) { + } else { + if (gstate.isDepthTestEnabled()) { + glstate.depthTest.enable(); + glstate.depthFunc.set(ztests[gstate.getDepthTestFunc()]); + glstate.depthWrite.set(gstate.isDepthWriteEnabled() ? GL_TRUE : GL_FALSE); + } else { + glstate.depthTest.disable(); + } + + // PSP color/alpha mask is per bit but we can only support per byte. + // But let's do that, at least. And let's try a threshold. bool rmask = (gstate.pmskc & 0xFF) < 128; bool gmask = ((gstate.pmskc >> 8) & 0xFF) < 128; bool bmask = ((gstate.pmskc >> 16) & 0xFF) < 128; bool amask = (gstate.pmska & 0xFF) < 128; glstate.colorMask.set(rmask, gmask, bmask, amask); - } - // Stencil test - if (!gstate.isModeClear() && gstate.isStencilTestEnabled()) { - glstate.stencilTest.enable(); - glstate.stencilFunc.set(ztests[gstate.stenciltest & 0x7], // func - (gstate.stenciltest >> 8) & 0xFF, // ref - (gstate.stenciltest >> 16) & 0xFF); // mask - glstate.stencilOp.set(stencilOps[gstate.stencilop & 0x7], // stencil fail - stencilOps[(gstate.stencilop >> 8) & 0x7], // depth fail - stencilOps[(gstate.stencilop >> 16) & 0x7]); - } else if (gstate.isModeClear()) { - glstate.stencilTest.enable(); - glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE); - glstate.stencilFunc.set(GL_ALWAYS, 0, 0xFF); - } else { - glstate.stencilTest.disable(); + if (gstate.isStencilTestEnabled()) { + glstate.stencilTest.enable(); + glstate.stencilFunc.set(ztests[gstate.stenciltest & 0x7], // func + (gstate.stenciltest >> 8) & 0xFF, // ref + (gstate.stenciltest >> 16) & 0xFF); // mask + glstate.stencilOp.set(stencilOps[gstate.stencilop & 0x7], // stencil fail + stencilOps[(gstate.stencilop >> 8) & 0x7], // depth fail + stencilOps[(gstate.stencilop >> 16) & 0x7]); // depth pass + } else { + glstate.stencilTest.disable(); + } } - - // Dither - glstate.dither.set(gstate.ditherEnable & 1); - - bool wantDepthWrite = gstate.isModeClear() || gstate.isDepthWriteEnabled(); - glstate.depthWrite.set(wantDepthWrite ? GL_TRUE : GL_FALSE); } void TransformDrawEngine::UpdateViewportAndProjection() { @@ -236,7 +241,7 @@ void TransformDrawEngine::UpdateViewportAndProjection() { if (throughmode) { // No viewport transform here. Let's experiment with using region. glstate.viewport.set((0 + regionX1) * renderWidthFactor, (0 - regionY1) * renderHeightFactor, (regionX2 - regionX1) * renderWidthFactor, (regionY2 - regionY1) * renderHeightFactor); - glstate.depthRange.set(1.0, 0.0); + glstate.depthRange.set(0.0f, 1.0f); } else { // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. float vpXa = getFloat24(gstate.viewportx1); @@ -269,8 +274,8 @@ void TransformDrawEngine::UpdateViewportAndProjection() { // Sadly, as glViewport takes integers, we will not be able to support sub pixel offsets this way. But meh. // shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); - float zScale = getFloat24(gstate.viewportz1) / 65535.f; - float zOff = getFloat24(gstate.viewportz2) / 65535.f; + float zScale = getFloat24(gstate.viewportz1) / 65535.0f; + float zOff = getFloat24(gstate.viewportz2) / 65535.0f; float depthRangeMin = zOff - zScale; float depthRangeMax = zOff + zScale; glstate.depthRange.set(depthRangeMin, depthRangeMax); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 6487e9ce36aa..8b4d202e2da5 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -18,11 +18,12 @@ #include #include -#include "../../Core/MemMap.h" -#include "../ge_constants.h" -#include "../GPUState.h" -#include "TextureCache.h" -#include "../Core/Config.h" +#include "Core/MemMap.h" +#include "GPU/ge_constants.h" +#include "GPU/GPUState.h" +#include "GPU/GLES/TextureCache.h" +#include "GPU/GLES/Framebuffer.h" +#include "Core/Config.h" // If a texture hasn't been seen for this many frames, get rid of it. #define TEXTURE_KILL_AGE 200 @@ -79,7 +80,7 @@ void TextureCache::Clear(bool delete_them) { // Removes old textures. void TextureCache::Decimate() { glBindTexture(GL_TEXTURE_2D, 0); - for (TexCache::iterator iter = cache.begin(), end = cache.end(); iter != end; ) { + for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ) { if (iter->second.lastFrame + TEXTURE_KILL_AGE < gpuStats.numFrames) { glDeleteTextures(1, &iter->second.texture); cache.erase(iter++); @@ -131,21 +132,21 @@ TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { return 0; } -void TextureCache::NotifyFramebuffer(u32 address, FBO *fbo) { +void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer) { // Must be in VRAM so | 0x04000000 it is. TexCacheEntry *entry = GetEntryAt(address | 0x04000000); if (entry) { DEBUG_LOG(HLE, "Render to texture detected at %08x!", address); - if (!entry->fbo) - entry->fbo = fbo; + if (!entry->framebuffer) + entry->framebuffer = framebuffer; // TODO: Delete the original non-fbo texture too. } } -void TextureCache::NotifyFramebufferDestroyed(u32 address, FBO *fbo) { +void TextureCache::NotifyFramebufferDestroyed(u32 address, VirtualFramebuffer *fbo) { TexCacheEntry *entry = GetEntryAt(address | 0x04000000); - if (entry && entry->fbo) { - entry->fbo = 0; + if (entry && entry->framebuffer) { + entry->framebuffer = 0; } } @@ -676,7 +677,6 @@ void TextureCache::SetTexture() { } bool hasClut = formatUsesClut[format]; - const u8 *texptr = Memory::GetPointer(texaddr); u64 cachekey = texaddr; u32 clutformat, clutaddr; @@ -684,6 +684,8 @@ void TextureCache::SetTexture() { clutformat = gstate.clutformat & 3; clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); cachekey |= (u64)clutaddr << 32; + } else { + clutaddr = 0; } int maxLevel = ((gstate.texmode >> 16) & 0x7); @@ -693,23 +695,29 @@ void TextureCache::SetTexture() { TexCache::iterator iter = cache.find(cachekey); TexCacheEntry *entry = NULL; gstate_c.flipTexture = false; + gstate_c.skipDrawReason &= ~SKIPDRAW_BAD_FB_TEXTURE; if (iter != cache.end()) { entry = &iter->second; // Check for FBO - slow! - if (entry->fbo) { - int w = 1 << (gstate.texsize[0] & 0xf); - int h = 1 << ((gstate.texsize[0] >> 8) & 0xf); - - fbo_bind_color_as_texture(entry->fbo, 0); - UpdateSamplingParams(*entry, false); + if (entry->framebuffer) { + entry->framebuffer->usageFlags |= FB_USAGE_TEXTURE; + if (entry->framebuffer->fbo) + { + fbo_bind_color_as_texture(entry->framebuffer->fbo, 0); + } + else { + glBindTexture(GL_TEXTURE_2D, 0); + gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; + } - int fbow, fboh; - fbo_get_dimensions(entry->fbo, &fbow, &fboh); + UpdateSamplingParams(*entry, false); // This isn't right. - gstate_c.curTextureWidth = w; //w; //RoundUpToPowerOf2(fbow); - gstate_c.curTextureHeight = h; // RoundUpToPowerOf2(fboh); + gstate_c.curTextureWidth = entry->framebuffer->width; + gstate_c.curTextureHeight = entry->framebuffer->height; + int h = 1 << ((gstate.texsize[0] >> 8) & 0xf); + gstate_c.actualTextureHeight = h; gstate_c.flipTexture = true; entry->lastFrame = gpuStats.numFrames; return; @@ -725,7 +733,7 @@ void TextureCache::SetTexture() { entry->hash != texhash || entry->format != format || entry->maxLevel != maxLevel || - ((format >= GE_TFMT_CLUT4 && format <= GE_TFMT_CLUT32) && + (hasClut && (entry->clutformat != clutformat || entry->clutaddr != clutaddr || entry->cluthash != Memory::Read_U32(entry->clutaddr)))) @@ -805,14 +813,14 @@ void TextureCache::SetTexture() { entry->hash = texhash; entry->format = format; entry->lastFrame = gpuStats.numFrames; - entry->fbo = 0; + entry->framebuffer = 0; entry->maxLevel = maxLevel; entry->lodBias = 0.0f; - if (format >= GE_TFMT_CLUT4 && format <= GE_TFMT_CLUT32) { + if (hasClut) { entry->clutformat = clutformat; - entry->clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); + entry->clutaddr = clutaddr; entry->cluthash = Memory::Read_U32(entry->clutaddr); } else { entry->clutaddr = 0; @@ -1151,14 +1159,8 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state) } u32 clutformat = gstate.clutformat & 3; - u32 clutaddr = GetClutAddr(clutformat == GE_CMODE_32BIT_ABGR8888 ? 4 : 2); const u8 *texptr = Memory::GetPointer(texaddr); - u32 texhash = texptr ? MiniHash((const u32*)texptr) : 0; - - u64 cachekey = texaddr ^ texhash; - if (formatUsesClut[format]) - cachekey |= (u64) clutaddr << 32; int bufw = gstate.texbufwidth[0] & 0x3ff; @@ -1173,8 +1175,6 @@ bool TextureCache::DecodeTexture(u8* output, GPUgstate state) // TODO: Look into using BGRA for 32-bit textures when the GL_EXT_texture_format_BGRA8888 extension is available, as it's faster than RGBA on some chips. - // TODO: Actually decode the mipmaps. - switch (format) { case GE_TFMT_CLUT4: diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index 764ad6373524..62008ec0ddeb 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -21,6 +21,8 @@ #include "gfx_es2/fbo.h" #include "GPU/GPUState.h" +struct VirtualFramebuffer; + class TextureCache { public: @@ -36,16 +38,16 @@ class TextureCache // FramebufferManager keeps TextureCache updated about what regions of memory // are being rendered to. This is barebones so far. - void NotifyFramebuffer(u32 address, FBO *fbo); - void NotifyFramebufferDestroyed(u32 address, FBO *fbo); + void NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer); + void NotifyFramebufferDestroyed(u32 address, VirtualFramebuffer *framebuffer); size_t NumLoadedTextures() const { return cache.size(); } bool DecodeTexture(u8 *output, GPUgstate state); + private: - struct TexCacheEntry { // After marking STATUS_UNRELIABLE, if it stays the same this many frames we'll trust it again. const static int FRAMES_REGAIN_TRUST = 1000; @@ -60,14 +62,14 @@ class TextureCache int status; u32 addr; u32 hash; - FBO *fbo; // if null, not sourced from an FBO. + VirtualFramebuffer *framebuffer; // if null, not sourced from an FBO. u32 sizeInRAM; int lastFrame; int numFrames; u32 framesUntilNextFullHash; u8 format; - u8 clutformat; u16 dim; + u8 clutformat; u32 clutaddr; u32 cluthash; u32 texture; //GLuint @@ -102,7 +104,7 @@ class TextureCache u32 *clutBuf32; u16 *clutBuf16; - int lastBoundTexture; + u32 lastBoundTexture; float maxAnisotropyLevel; }; diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 85fa40b63166..ee9687fd057b 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -53,12 +53,14 @@ enum { }; TransformDrawEngine::TransformDrawEngine() - : numDrawCalls(0), - collectedVerts(0), + : collectedVerts(0), prevPrim_(-1), lastVType_(-1), curVbo_(0), - shaderManager_(0) { + shaderManager_(0), + textureCache_(0), + framebufferManager_(0), + numDrawCalls(0) { // Allocate nicely aligned memory. Maybe graphics drivers will // appreciate it. // All this is a LOT of memory, need to see if we can cut down somehow. @@ -638,7 +640,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( transformed[index].fog = fogCoef; memcpy(&transformed[index].u, uv, 2 * sizeof(float)); if (gstate_c.flipTexture) - transformed[index].v = 1.0f - transformed[index].v * 2.0f; + transformed[index].v = 1.0f - transformed[index].v; //(float)gstate_c.actualTextureHeight / gstate_c.curTextureHeight - transformed[index].v; for (int i = 0; i < 4; i++) { transformed[index].color0[i] = c0[i] * 255.0f; } @@ -658,10 +660,6 @@ void TransformDrawEngine::SoftwareTransformAndDraw( numTrans = vertexCount; drawIndexed = true; } else { - // Temporary storage for RECTANGLES emulation - float v2[3] = {0}; - float uv2[2] = {0}; - numTrans = 0; drawBuffer = transformedExpanded; TransformedVertex *trans = &transformedExpanded[0]; @@ -928,7 +926,7 @@ void TransformDrawEngine::Flush() { gpuStats.numFlushes++; - gpuStats.numTrackedVertexArrays = vai_.size(); + gpuStats.numTrackedVertexArrays = (int)vai_.size(); // TODO: This should not be done on every drawcall, we should collect vertex data // until critical state changes. That's when we draw (flush). diff --git a/GPU/GLES/VertexDecoder.cpp b/GPU/GLES/VertexDecoder.cpp index 6e68bdec48ed..75614f67eacd 100644 --- a/GPU/GLES/VertexDecoder.cpp +++ b/GPU/GLES/VertexDecoder.cpp @@ -197,7 +197,7 @@ void VertexDecoder::Step_Color5551() const c[0] = Convert5To8(cdata & 0x1f); c[1] = Convert5To8((cdata>>5) & 0x1f); c[2] = Convert5To8((cdata>>10) & 0x1f); - c[3] = (cdata>>15) ? 255 : 0; + c[3] = (cdata>>15) ? 255.0f : 0.0f; } void VertexDecoder::Step_Color4444() const @@ -222,15 +222,15 @@ void VertexDecoder::Step_Color565Morph() const { float w = gstate_c.morphWeights[n]; u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); - col[0] += w * (cdata & 0x1f) / 31.f; - col[1] += w * ((cdata>>5) & 0x3f) / 63.f; - col[2] += w * ((cdata>>11) & 0x1f) / 31.f; + col[0] += w * (cdata & 0x1f) / 31.0f; + col[1] += w * ((cdata>>5) & 0x3f) / 63.0f; + col[2] += w * ((cdata>>11) & 0x1f) / 31.0f; } u8 *c = decoded_ + decFmt.c0off; for (int i = 0; i < 3; i++) { c[i] = (u8)(col[i] * 255.0f); } - c[3] = 255; + c[3] = 255.0f; } void VertexDecoder::Step_Color5551Morph() const @@ -240,9 +240,9 @@ void VertexDecoder::Step_Color5551Morph() const { float w = gstate_c.morphWeights[n]; u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); - col[0] += w * (cdata & 0x1f) / 31.f; - col[1] += w * ((cdata>>5) & 0x1f) / 31.f; - col[2] += w * ((cdata>>10) & 0x1f) / 31.f; + col[0] += w * (cdata & 0x1f) / 31.0f; + col[1] += w * ((cdata>>5) & 0x1f) / 31.0f; + col[2] += w * ((cdata>>10) & 0x1f) / 31.0f; col[3] += w * ((cdata>>15) ? 1.0f : 0.0f); } u8 *c = decoded_ + decFmt.c0off; @@ -259,7 +259,7 @@ void VertexDecoder::Step_Color4444Morph() const float w = gstate_c.morphWeights[n]; u16 cdata = *(u16*)(ptr_ + onesize_*n + coloff); for (int j = 0; j < 4; j++) - col[j] += w * ((cdata >> (j * 4)) & 0xF) / 15.f; + col[j] += w * ((cdata >> (j * 4)) & 0xF) / 15.0f; } u8 *c = decoded_ + decFmt.c0off; for (int i = 0; i < 4; i++) { @@ -346,7 +346,7 @@ void VertexDecoder::Step_NormalS16Morph() const multiplier = -multiplier; } const s16 *sv = (const s16 *)(ptr_ + onesize_*n + nrmoff); - multiplier *= (1.0f/32767.f); + multiplier *= (1.0f/32767.0f); for (int j = 0; j < 3; j++) normal[j] += sv[j] * multiplier; } diff --git a/GPU/GLES/VertexDecoder.h b/GPU/GLES/VertexDecoder.h index 0be7140f138b..496e29b24cb7 100644 --- a/GPU/GLES/VertexDecoder.h +++ b/GPU/GLES/VertexDecoder.h @@ -191,20 +191,43 @@ class VertexReader void ReadPos(float pos[3]) { switch (decFmt_.posfmt) { case DEC_FLOAT_3: - memcpy(pos, data_ + decFmt_.posoff, 12); + { + const float *f = (const float *)(data_ + decFmt_.posoff); + memcpy(pos, f, 12); + if (isThrough()) { + // Integer value passed in a float. Wraps and all, required for Monster Hunter. + pos[2] = (float)((u16)(s32)pos[2]) * (1.0f / 65535.0f); + } + } break; case DEC_S16_3: { - const s16 *p = (s16 *)(data_ + decFmt_.posoff); - for (int i = 0; i < 3; i++) - pos[i] = p[i] * (1.f / 32768.f); + // X and Y are signed 16 bit, Z is unsigned 16 bit + const s16 *s = (const s16 *)(data_ + decFmt_.posoff); + const u16 *u = (const u16 *)(data_ + decFmt_.posoff); + if (isThrough()) { + for (int i = 0; i < 2; i++) + pos[i] = s[i]; + pos[2] = u[2] * (1.0f / 65535.0f); + } else { + for (int i = 0; i < 3; i++) + pos[i] = s[i] * (1.f / 32767.f); + } } break; case DEC_S8_3: { - const s8 *p = (s8 *)(data_ + decFmt_.posoff); - for (int i = 0; i < 3; i++) - pos[i] = p[i] * (1.f / 128.f); + // X and Y are signed 8 bit, Z is unsigned 8 bit + const s8 *b = (const s8 *)(data_ + decFmt_.posoff); + const u8 *u = (const u8 *)(data_ + decFmt_.posoff); + if (isThrough()) { + for (int i = 0; i < 2; i++) + pos[i] = b[i]; + pos[2] = u[2] / 255.0f; + } else { + for (int i = 0; i < 3; i++) + pos[i] = b[i] * (1.f / 127.f); + } } break; default: @@ -216,20 +239,25 @@ class VertexReader void ReadNrm(float nrm[3]) { switch (decFmt_.nrmfmt) { case DEC_FLOAT_3: - memcpy(nrm, data_ + decFmt_.nrmoff, 12); + //memcpy(nrm, data_ + decFmt_.nrmoff, 12); + { + const float *f = (const float *)(data_ + decFmt_.nrmoff); + for (int i = 0; i < 3; i++) + nrm[i] = f[i] ; + } break; case DEC_S16_3: { - const s16 *p = (s16 *)(data_ + decFmt_.nrmoff); + const s16 *s = (const s16 *)(data_ + decFmt_.nrmoff); for (int i = 0; i < 3; i++) - nrm[i] = p[i] * (1.f / 32768.f); + nrm[i] = s[i] * (1.f / 32767.f); } break; case DEC_S8_3: { - const s8 *p = (s8 *)(data_ + decFmt_.nrmoff); + const s8 *b = (const s8 *)(data_ + decFmt_.nrmoff); for (int i = 0; i < 3; i++) - nrm[i] = p[i] * (1.f / 128.0f); + nrm[i] = b[i] * (1.f / 127.f); } break; default: @@ -239,23 +267,29 @@ class VertexReader } void ReadUV(float uv[2]) { - const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); - const u16 *s = (const u16 *)(data_ + decFmt_.uvoff); - const float *f = (const float *)(data_ + decFmt_.uvoff); switch (decFmt_.uvfmt) { case DEC_U8_2: - uv[0] = b[0] * (1.f / 128.f); - uv[1] = b[1] * (1.f / 128.f); + { + const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); + uv[0] = b[0] * (1.f / 128.f); + uv[1] = b[1] * (1.f / 128.f); + } break; case DEC_U16_2: - uv[0] = s[0] * (1.f / 32768.f); - uv[1] = s[1] * (1.f / 32768.f); + { + const u16 *s = (const u16 *)(data_ + decFmt_.uvoff); + uv[0] = s[0] * (1.f / 32768.f); + uv[1] = s[1] * (1.f / 32768.f); + } break; case DEC_FLOAT_2: - uv[0] = f[0] * 2.0f; - uv[1] = f[1] * 2.0f; + { + const float *f = (const float *)(data_ + decFmt_.uvoff); + uv[0] = f[0] * 2.f; + uv[1] = f[1] * 2.f; + } break; case DEC_U16A_2: @@ -275,13 +309,14 @@ class VertexReader switch (decFmt_.c0fmt) { case DEC_U8_4: { - const u8 *p = (const u8 *)(data_ + decFmt_.c0off); + const u8 *b = (const u8 *)(data_ + decFmt_.c0off); for (int i = 0; i < 4; i++) - color[i] = p[i] * (1.f / 255.f); + color[i] = b[i] * (1.f / 255.f); } break; case DEC_FLOAT_4: - memcpy(color, data_ + decFmt_.c0off, 16); break; + memcpy(color, data_ + decFmt_.c0off, 16); + break; default: ERROR_LOG(G3D, "Reader: Unsupported C0 Format"); break; @@ -292,13 +327,14 @@ class VertexReader switch (decFmt_.c1fmt) { case DEC_U8_4: { - const u8 *p = (const u8 *)(data_ + decFmt_.c1off); + const u8 *b = (const u8 *)(data_ + decFmt_.c1off); for (int i = 0; i < 3; i++) - color[i] = p[i] * (1.f / 255.f); + color[i] = b[i] * (1.f / 255.f); } break; case DEC_FLOAT_4: - memcpy(color, data_ + decFmt_.c1off, 12); break; + memcpy(color, data_ + decFmt_.c1off, 12); + break; default: ERROR_LOG(G3D, "Reader: Unsupported C1 Format"); break; @@ -307,7 +343,7 @@ class VertexReader void ReadWeights(float weights[8]) { const float *f = (const float *)(data_ + decFmt_.w0off); - const u8 *p = (const u8 *)(data_ + decFmt_.w0off); + const u8 *b = (const u8 *)(data_ + decFmt_.w0off); const u16 *s = (const u16 *)(data_ + decFmt_.w0off); switch (decFmt_.w0fmt) { case DEC_FLOAT_1: @@ -315,12 +351,12 @@ class VertexReader case DEC_FLOAT_3: case DEC_FLOAT_4: for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++) - weights[i] = f[i] * 2.0; + weights[i] = f[i] * 2.f; break; - case DEC_U8_1: weights[0] = p[0] * (1.f / 128.f); break; - case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = p[i] * (1.f / 128.f); break; - case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = p[i] * (1.f / 128.f); break; - case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = p[i] * (1.f / 128.f); break; + case DEC_U8_1: weights[0] = b[0] * (1.f / 128.f); break; + case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = b[i] * (1.f / 128.f); break; + case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = b[i] * (1.f / 128.f); break; + case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = b[i] * (1.f / 128.f); break; case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break; case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break; case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break; @@ -330,8 +366,8 @@ class VertexReader break; } - f = (const float*)(data_ + decFmt_.w1off); - p = (const u8 *)(data_ + decFmt_.w1off); + f = (const float *)(data_ + decFmt_.w1off); + b = (const u8 *)(data_ + decFmt_.w1off); s = (const u16 *)(data_ + decFmt_.w1off); switch (decFmt_.w1fmt) { case 0: @@ -342,8 +378,12 @@ class VertexReader case DEC_FLOAT_3: case DEC_FLOAT_4: for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++) - weights[i+4] = f[i] * 2.0; + weights[i+4] = f[i] * 2.f; break; + case DEC_U8_1: weights[4] = b[0] * (1.f / 128.f); break; + case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i+4] = b[i] * (1.f / 128.f); break; + case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i+4] = b[i] * (1.f / 128.f); break; + case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i+4] = b[i] * (1.f / 128.f); break; case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break; case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index d5cb6c1e2cfe..ed2f74d26630 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -133,6 +133,13 @@ void GenerateVertexShader(int prim, char *buffer) { WRITE(p, "precision highp float;\n"); #elif !defined(FORCE_OPENGL_2_0) WRITE(p, "#version 110\n"); + // Remove lowp/mediump in non-mobile implementations + WRITE(p, "#define lowp\n"); + WRITE(p, "#define mediump\n"); +#else + // Need to remove lowp/mediump for Mac + WRITE(p, "#define lowp\n"); + WRITE(p, "#define mediump\n"); #endif int lmode = (gstate.lmode & 1) && (gstate.lightingEnable & 1); @@ -169,13 +176,13 @@ void GenerateVertexShader(int prim, char *buffer) { if (doTexture) WRITE(p, "attribute vec2 a_texcoord;\n"); if (hasColor) { - WRITE(p, "attribute vec4 a_color0;\n"); + WRITE(p, "attribute lowp vec4 a_color0;\n"); if (lmode && !hwXForm) // only software transform supplies color1 as vertex data - WRITE(p, "attribute vec3 a_color1;\n"); + WRITE(p, "attribute lowp vec3 a_color1;\n"); } if (hwXForm && hasNormal) - WRITE(p, "attribute vec3 a_normal;\n"); + WRITE(p, "attribute mediump vec3 a_normal;\n"); if (gstate.isModeThrough()) { WRITE(p, "uniform mat4 u_proj_through;\n"); @@ -185,7 +192,7 @@ void GenerateVertexShader(int prim, char *buffer) { } if (hwXForm || !hasColor) - WRITE(p, "uniform vec4 u_matambientalpha;\n"); // matambient + matalpha + WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha if (enableFog) { WRITE(p, "uniform vec2 u_fogcoef;\n"); @@ -206,12 +213,12 @@ void GenerateVertexShader(int prim, char *buffer) { } } if (gstate.lightingEnable & 1) { - WRITE(p, "uniform vec4 u_ambient;\n"); + WRITE(p, "uniform lowp vec4 u_ambient;\n"); if ((gstate.materialupdate & 2) == 0) - WRITE(p, "uniform vec3 u_matdiffuse;\n"); + WRITE(p, "uniform lowp vec3 u_matdiffuse;\n"); // if ((gstate.materialupdate & 4) == 0) - WRITE(p, "uniform vec4 u_matspecular;\n"); // Specular coef is contained in alpha - WRITE(p, "uniform vec3 u_matemissive;\n"); + WRITE(p, "uniform lowp vec4 u_matspecular;\n"); // Specular coef is contained in alpha + WRITE(p, "uniform lowp vec3 u_matemissive;\n"); } for (int i = 0; i < 4; i++) { if (doLight[i] != LIGHT_OFF) { @@ -222,15 +229,15 @@ void GenerateVertexShader(int prim, char *buffer) { } if (doLight[i] == LIGHT_FULL) { // These are needed for the full thing - WRITE(p, "uniform vec3 u_lightambient%i;\n", i); - WRITE(p, "uniform vec3 u_lightdiffuse%i;\n", i); - WRITE(p, "uniform vec3 u_lightspecular%i;\n", i); + WRITE(p, "uniform lowp vec3 u_lightambient%i;\n", i); + WRITE(p, "uniform lowp vec3 u_lightdiffuse%i;\n", i); + WRITE(p, "uniform lowp vec3 u_lightspecular%i;\n", i); } } } - WRITE(p, "varying vec4 v_color0;\n"); - if (lmode) WRITE(p, "varying vec3 v_color1;\n"); + WRITE(p, "varying lowp vec4 v_color0;\n"); + if (lmode) WRITE(p, "varying lowp vec3 v_color1;\n"); if (doTexture) WRITE(p, "varying vec2 v_texcoord;\n"); if (enableFog) WRITE(p, "varying float v_fogdepth;\n"); @@ -290,9 +297,9 @@ void GenerateVertexShader(int prim, char *buffer) { // Step 2: Color/Lighting if (hasColor) { - WRITE(p, " vec3 unlitColor = a_color0.rgb;\n"); + WRITE(p, " lowp vec3 unlitColor = a_color0.rgb;\n"); } else { - WRITE(p, " vec3 unlitColor = vec3(1.0, 1.0, 1.0);\n"); + WRITE(p, " lowp vec3 unlitColor = vec3(1.0, 1.0, 1.0);\n"); } // TODO: Declare variables for dots for shade mapping if needed. @@ -301,8 +308,8 @@ void GenerateVertexShader(int prim, char *buffer) { const char *specular = (gstate.materialupdate & 4) ? "unlitColor" : "u_matspecular.rgb"; if (gstate.lightingEnable & 1) { - WRITE(p, " vec4 lightSum0 = u_ambient * %s + vec4(u_matemissive, 0.0);\n", ambient); - WRITE(p, " vec3 lightSum1 = vec3(0.0);\n"); + WRITE(p, " lowp vec4 lightSum0 = u_ambient * %s + vec4(u_matemissive, 0.0);\n", ambient); + WRITE(p, " lowp vec3 lightSum1 = vec3(0.0);\n"); } // Calculate lights if needed. If shade mapping is enabled, lights may need to be @@ -401,7 +408,7 @@ void GenerateVertexShader(int prim, char *buffer) { break; } if (flipV) - WRITE(p, " v_texcoord.y = 1.0 - v_texcoord.y;\n"); + WRITE(p, " v_texcoord.y = 1.0 - v_texcoord.y * 2.0;\n"); } // Compute fogdepth diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index ff050581ed82..5c5f98b38b6d 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -98,7 +98,7 @@ bool GPUCommon::InterpretList(DisplayList &list) #if defined(USING_QT_UI) if(host->GpuStep()) { - host->SendGPUWait(cmd); + host->SendGPUWait(cmd, list.pc, &gstate); } #endif u32 diff = op ^ gstate.cmdmem[cmd]; @@ -107,7 +107,7 @@ bool GPUCommon::InterpretList(DisplayList &list) if (dumpThisFrame_) { char temp[256]; GeDisassembleOp(list.pc, op, prev, temp); - NOTICE_LOG(G3D, "%s", temp); + NOTICE_LOG(HLE, "%s", temp); } gstate.cmdmem[cmd] = op; // crashes if I try to put the whole op there?? diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 8feb9cb5699e..88aa151a8d70 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -23,6 +23,7 @@ class GPUCommon : public GPUInterface virtual u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head); virtual int listStatus(int listid); virtual void DoState(PointerWrap &p); + virtual bool FramebufferDirty() { return true; } protected: typedef std::deque DisplayListQueue; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 4edb5de1c5b8..f558e3ceb8f2 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -102,6 +102,7 @@ class GPUInterface // Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*. virtual void Resized() = 0; + virtual bool FramebufferDirty() = 0; // Debugging virtual void DumpNextFrame() = 0; diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index 75474fcb6fa3..35a5be2b7d02 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -64,6 +64,9 @@ void InitGfxState() case GPU_GLES: gpu = new GLES_GPU(); break; + case GPU_SOFTWARE: + gpu = new NullGPU(); + break; } } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index ca036b25063a..853a05b4e891 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -209,6 +209,8 @@ struct GPUgstate int getDepthTestFunc() const { return ztestfunc & 0x7; } bool isFogEnabled() const { return fogEnable & 1; } bool isStencilTestEnabled() const { return stencilTestEnable & 1; } + bool isAlphaBlendEnabled() const { return alphaBlendEnable & 1; } + bool isDitherEnabled() const { return ditherEnable & 1; } // UV gen int getUVGenMode() const { return texmapmode & 3;} // 2 bits @@ -224,7 +226,13 @@ struct GPUgstate // Real data in the context ends here }; - + +enum SkipDrawReasonFlags { + SKIPDRAW_SKIPFRAME = 1, + SKIPDRAW_NON_DISPLAYED_FB = 2, // Skip drawing to FBO:s that have not been displayed. + SKIPDRAW_BAD_FB_TEXTURE = 4, +}; + // The rest is cached simplified/converted data for fast access. // Does not need to be saved when saving/restoring context. struct GPUStateCache @@ -236,6 +244,8 @@ struct GPUStateCache bool textureChanged; + int skipDrawReason; + float uScale,vScale; float uOff,vOff; bool flipTexture; @@ -249,6 +259,7 @@ struct GPUStateCache u32 curTextureWidth; u32 curTextureHeight; + u32 actualTextureHeight; float vpWidth; float vpHeight; diff --git a/Qt/Core.pro b/Qt/Core.pro index d9f62601856a..aa21dee9c01f 100755 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -4,6 +4,15 @@ TARGET = Core TEMPLATE = lib CONFIG += staticlib +version.target = ../git-version.cpp +contains(QMAKE_HOST.os, "Windows") { version.commands = $$PWD/../Windows/git-version-gen.cmd } +else { version.commands = $$PWD/git-version-gen.sh } +version.depends = ../.git + +QMAKE_EXTRA_TARGETS += version +PRE_TARGETDEPS += ../git-version.cpp +SOURCES += ../git-version.cpp + include(Settings.pri) INCLUDEPATH += ../native ../Core/MIPS ../ @@ -18,6 +27,11 @@ x86 { HEADERS += ../Core/MIPS/x86/*.h } +win32 { + SOURCES += ../Windows/OpenGLBase.cpp + HEADERS += ../Windows/OpenGLBase.h +} + SOURCES += ../Core/CPU.cpp \ # Core ../Core/Config.cpp \ ../Core/Core.cpp \ @@ -28,12 +42,14 @@ SOURCES += ../Core/CPU.cpp \ # Core ../Core/MemMapFunctions.cpp \ ../Core/PSPLoaders.cpp \ ../Core/PSPMixer.cpp \ + ../Core/Reporting.cpp \ ../Core/SaveState.cpp \ ../Core/System.cpp \ ../Core/Debugger/*.cpp \ ../Core/Dialog/*.cpp \ ../Core/ELF/*.cpp \ ../Core/FileSystems/*.cpp \ + ../Core/Font/*.cpp \ ../Core/HLE/*.cpp \ ../Core/HW/*.cpp \ ../Core/MIPS/*.cpp \ @@ -57,12 +73,14 @@ HEADERS += ../Core/CPU.h \ ../Core/MemMap.h \ ../Core/PSPLoaders.h \ ../Core/PSPMixer.h \ + ../Core/Reporting.h \ ../Core/SaveState.h \ ../Core/System.h \ ../Core/Debugger/*.h \ ../Core/Dialog/*.h \ ../Core/ELF/*.h \ ../Core/FileSystems/*.h \ + ../Core/Font/*.h \ ../Core/HLE/*.h \ ../Core/HW/*.h \ ../Core/MIPS/*.h \ diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 77b2c18df835..935912f4215f 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -10,7 +10,14 @@ include(Settings.pri) # Libs symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib -lcone -leikcore -lavkon -lezlib blackberry: LIBS += -L. -lCore -lCommon -lNative -lscreen -lsocket -lstdc++ -win32: LIBS += -L$$OUT_PWD/release -lCore -lCommon -lNative -lwinmm -lws2_32 -lkernel32 -luser32 -lgdi32 -lshell32 -lcomctl32 -ldsound -lxinput +win32 { + CONFIG(release, debug|release) { + LIBS += -L$$OUT_PWD/release + } else { + LIBS += -L$$OUT_PWD/debug + } + LIBS += -lCore -lCommon -lNative -lwinmm -lws2_32 -lkernel32 -luser32 -lgdi32 -lshell32 -lcomctl32 -ldsound -lxinput +} linux: LIBS += -L. -lCore -lCommon -lNative linux:!mobile_platform { @@ -22,8 +29,6 @@ linux:!mobile_platform { } } -TRANSLATIONS = $$files(languages/ppsspp_*.ts) - # Main SOURCES += ../native/base/QtMain.cpp HEADERS += ../native/base/QtMain.h @@ -51,10 +56,21 @@ mobile_platform { INCLUDEPATH += ../Qt } +# Translations +TRANSLATIONS = $$files(languages/ppsspp_*.ts) + +lang.name = lrelease ${QMAKE_FILE_IN} +lang.input = TRANSLATIONS +lang.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.qm +lang.commands = $$[QT_INSTALL_BINS]/lrelease ${QMAKE_FILE_IN} +lang.CONFIG = no_link +QMAKE_EXTRA_COMPILERS += lang +PRE_TARGETDEPS += compiler_lang_make_all + # Packaging symbian { deploy.pkg_prerules = "$${LITERAL_HASH}{\"PPSSPP\"}, (0xE0095B1D), 0, 6, 1, TYPE=SA" "%{\"Qtness\"}" ":\"Qtness\"" - assets.sources = ../android/assets/ui_atlas.zim ../assets/ppge_atlas.zim + assets.sources = ../android/assets/ui_atlas.zim ../assets/ppge_atlas.zim ../assets/flash assets.path = E:/PPSSPP DEPLOYMENT += deploy assets ICON = ../assets/icon.svg diff --git a/Qt/QtHost.cpp b/Qt/QtHost.cpp index ca55e43879fe..a5f04cb81806 100644 --- a/Qt/QtHost.cpp +++ b/Qt/QtHost.cpp @@ -28,6 +28,7 @@ recursive_mutex m_hGPUStepMutex; QtHost::QtHost(MainWindow *mainWindow_) : mainWindow(mainWindow_) , m_GPUStep(false) + , m_GPUFlag(0) { QObject::connect(this,SIGNAL(BootDoneSignal()),mainWindow,SLOT(Boot())); } @@ -42,7 +43,7 @@ void QtHost::ShutdownGL() void QtHost::SetWindowTitle(const char *message) { - QString title = "PPSSPP " + QString(PPSSPP_VERSION_STR) + " - " + QString::fromUtf8(message); + QString title = "PPSSPP " + QString(PPSSPP_GIT_VERSION) + " - " + QString::fromUtf8(message); mainWindow->setWindowTitle(title); } @@ -162,7 +163,7 @@ void QtHost::SendGPUStart() EmuThread_LockDraw(true); } -void QtHost::SendGPUWait(u32 cmd) +void QtHost::SendGPUWait(u32 cmd, u32 addr, void *data) { EmuThread_LockDraw(false); @@ -173,19 +174,36 @@ void QtHost::SendGPUWait(u32 cmd) } else if(m_GPUFlag == 0) { - mainWindow->GetDialogDisasm()->UpdateDisplayList(); mainWindow->GetDialogDisplaylist()->Update(); m_hGPUStepEvent.wait(m_hGPUStepMutex); } + else if(m_GPUFlag == 2 && addr == m_GPUData) + { + mainWindow->GetDialogDisasm()->UpdateDisplayList(); + mainWindow->GetDialogDisplaylist()->Update(); + m_hGPUStepEvent.wait(m_hGPUStepMutex); + } + else if(m_GPUFlag == 3 && (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE)) + { + GPUgstate *state = (GPUgstate*)data; + u32 texAddr = (state->texaddr[0] & 0xFFFFF0) | ((state->texbufwidth[0]<<8) & 0x0F000000); + if(texAddr == m_GPUData) + { + mainWindow->GetDialogDisasm()->UpdateDisplayList(); + mainWindow->GetDialogDisplaylist()->Update(); + m_hGPUStepEvent.wait(m_hGPUStepMutex); + } + } EmuThread_LockDraw(true); } -void QtHost::SetGPUStep(bool value, int flag) +void QtHost::SetGPUStep(bool value, int flag, int data) { m_GPUStep = value; m_GPUFlag = flag; + m_GPUData = data; } void QtHost::NextGPUStep() diff --git a/Qt/QtHost.h b/Qt/QtHost.h index 8f578c9fd5fc..3e9305694d0a 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -51,9 +51,9 @@ class QtHost : public QObject, public Host void SendCoreWait(bool); bool GpuStep(); - void SendGPUWait(u32 cmd); + void SendGPUWait(u32 cmd, u32 addr, void* data); void SendGPUStart(); - void SetGPUStep(bool value, int flag = 0); + void SetGPUStep(bool value, int flag = 0, int data = 0); void NextGPUStep(); signals: @@ -62,6 +62,7 @@ class QtHost : public QObject, public Host MainWindow* mainWindow; bool m_GPUStep; int m_GPUFlag; + int m_GPUData; }; #endif // QTAPP_H diff --git a/Qt/Settings.pri b/Qt/Settings.pri index 953e138ec321..7b9a23800817 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -1,5 +1,4 @@ DEFINES += USING_QT_UI -blackberry|symbian|contains(MEEGO_EDITION,harmattan): CONFIG += mobile_platform unix:!blackberry:!symbian:!macx: CONFIG += linux # Global specific @@ -8,7 +7,7 @@ INCLUDEPATH += ../ext/zlib ../native/ext/glew ../Common win32-msvc* { QMAKE_CXXFLAGS_RELEASE += /O2 /arch:SSE2 /fp:fast - DEFINES += _MBCS GLEW_STATIC NOMINMAX + DEFINES += _MBCS GLEW_STATIC NOMINMAX NODRAWTEXT _CRT_SECURE_NO_WARNINGS PRECOMPILED_HEADER = ../Windows/stdafx.h PRECOMPILED_SOURCE = ../Windows/stdafx.cpp } else { @@ -17,7 +16,8 @@ win32-msvc* { } # Arch specific -contains(QT_ARCH, i686)|contains(QT_ARCH, x86)|contains(QT_ARCH, x86_64)|contains(QT_ARCH, windows): { +xarch = $$find(QT_ARCH, "86") +contains(QT_ARCH, windows)|count(xarch, 1) { !win32-msvc*: QMAKE_CXXFLAGS += -msse2 CONFIG += x86 } @@ -25,8 +25,13 @@ else { # Assume ARM DEFINES += ARM CONFIG += arm } -mobile_platform: DEFINES += USING_GLES2 +gleslib = $$lower($$QMAKE_LIBS_OPENGL) +gleslib = $$find(gleslib, "gles") +!count(gleslib,0) { + DEFINES += USING_GLES2 + CONFIG += mobile_platform +} # Platform specific contains(MEEGO_EDITION,harmattan): DEFINES += MEEGO_EDITION_HARMATTAN "_SYS_UCONTEXT_H=1" diff --git a/Qt/ctrldisasmview.cpp b/Qt/ctrldisasmview.cpp index 92d43f8e51f6..3bf021f23ea8 100644 --- a/Qt/ctrldisasmview.cpp +++ b/Qt/ctrldisasmview.cpp @@ -33,26 +33,15 @@ void CtrlDisAsmView::keyPressEvent(QKeyEvent *e) { int page=(rect().bottom()/rowHeight)/2-1; - if(e->key() == Qt::Key_Down) + switch (e->key()) { - curAddress += align; - e->accept(); - } - else if(e->key() == Qt::Key_Up) - { - curAddress -= align; - e->accept(); - } - else if(e->key() == Qt::Key_PageDown) - { - curAddress += page*align; - e->accept(); - } - else if(e->key() == Qt::Key_PageUp) - { - curAddress -= page*align; - e->accept(); + case Qt::Key_Down: curAddress += align; break; + case Qt::Key_Up: curAddress -= align; break; + case Qt::Key_PageDown: curAddress += page*align; break; + case Qt::Key_PageUp: curAddress -= page*align; break; + default: QWidget::keyPressEvent(e); break; } + update(); } @@ -68,7 +57,6 @@ void CtrlDisAsmView::mousePressEvent(QMouseEvent *e) selecting=true; if (!oldselecting || (selection!=oldSelection)) redraw(); - e->accept(); } else { @@ -77,7 +65,6 @@ void CtrlDisAsmView::mousePressEvent(QMouseEvent *e) EmuThread_LockDraw(false); parentWindow->Update(); redraw(); - e->accept(); } } @@ -85,12 +72,11 @@ void CtrlDisAsmView::wheelEvent(QWheelEvent* e) { int numDegrees = e->delta() / 8; int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Horizontal) { - } else { + if (e->orientation() == Qt::Vertical) + { curAddress -= numSteps*align; - e->accept(); update(); - } + } } void CtrlDisAsmView::redraw() @@ -243,7 +229,7 @@ void CtrlDisAsmView::RenameFunction() } else { - QMessageBox::information(this,"Warning","No symbol selected",QMessageBox::Ok); + QMessageBox::information(this,tr("Warning"),tr("No symbol selected"),QMessageBox::Ok); } } @@ -265,20 +251,20 @@ void CtrlDisAsmView::paintEvent(QPaintEvent *) int width = rect().width(); int numRows=(rect().height()/rowHeight)/2+1; - QColor bgColor = QColor(0xFFFFFFFF); - QPen nullPen= QPen(bgColor); - QPen currentPen=QPen(QColor(0,0,0)); - QPen selPen=QPen(QColor(0xFF808080)); - QPen condPen=QPen(QColor(0xFFFF3020)); + QColor bgColor(0xFFFFFFFF); + QPen nullPen(bgColor); + QPen currentPen(QColor(0,0,0)); + QPen selPen(QColor(0xFF808080)); + QPen condPen(QColor(0xFFFF3020)); QBrush lbr; lbr.setColor(bgColor); - QBrush currentBrush=QBrush(QColor(0xFFFFEfE8)); - QBrush pcBrush=QBrush(QColor(0xFF70FF70)); + QBrush currentBrush(QColor(0xFFFFEfE8)); + QBrush pcBrush(QColor(0xFF70FF70)); - QFont normalFont = QFont("Arial", 10); - QFont boldFont = QFont("Arial", 10); - QFont alignedFont = QFont("Monospace", 10); + QFont normalFont("Arial", 10); + QFont boldFont("Arial", 10); + QFont alignedFont("Monospace", 10); boldFont.setBold(true); painter.setFont(normalFont); @@ -295,17 +281,17 @@ void CtrlDisAsmView::paintEvent(QPaintEvent *) int rowY1 = rect().bottom()/2 + rowHeight*i - rowHeight/2; int rowY2 = rect().bottom()/2 + rowHeight*i + rowHeight/2 - 1; - lbr.setColor(marker==address?QColor(0xFFFFEEE0):QColor(debugger->getColor(address))); + lbr.setColor((unsigned int)marker == address ? QColor(0xFFFFEEE0) : QColor(debugger->getColor(address))); QColor bg = lbr.color(); painter.setPen(nullPen); painter.drawRect(0,rowY1,16-1,rowY2-rowY1); - if (selecting && address == selection) + if (selecting && address == (unsigned int)selection) painter.setPen(selPen); else painter.setPen(i==0 ? currentPen : nullPen); - QBrush mojsBrush=QBrush(lbr.color()); + QBrush mojsBrush(lbr.color()); painter.setBrush(mojsBrush); if (address == debugger->getPC()) @@ -315,7 +301,7 @@ void CtrlDisAsmView::paintEvent(QPaintEvent *) painter.drawRect(16,rowY1,width-16-1,rowY2-rowY1); painter.setBrush(currentBrush); - QPen textPen = QPen(QColor(halfAndHalf(bg.rgba(),0))); + QPen textPen(QColor(halfAndHalf(bg.rgba(),0))); painter.setPen(textPen); painter.setFont(alignedFont); painter.drawText(17,rowY1-3+rowHeight,QString("%1").arg(address,8,16,QChar('0'))); @@ -412,7 +398,7 @@ void CtrlDisAsmView::paintEvent(QPaintEvent *) { painter.setPen(branches[i].conditional ? condPen : currentPen); int x=280+(branches[i].srcAddr%9)*8; - QPoint curPos = QPoint(x-2,branches[i].src); + QPoint curPos(x-2,branches[i].src); if (branches[i].dst-200) { diff --git a/Qt/ctrldisasmview.h b/Qt/ctrldisasmview.h index dd1bce826bc1..d60be7ac77b9 100644 --- a/Qt/ctrldisasmview.h +++ b/Qt/ctrldisasmview.h @@ -12,7 +12,6 @@ class CtrlDisAsmView : public QWidget Q_OBJECT public: explicit CtrlDisAsmView(QWidget *parent = 0); - void redraw(); void setAlign(int l) @@ -73,7 +72,6 @@ class CtrlDisAsmView : public QWidget void mousePressEvent(QMouseEvent *e); void keyPressEvent(QKeyEvent *); void wheelEvent(QWheelEvent *e); -signals: public slots: void CopyAddress(); diff --git a/Qt/ctrlmemview.cpp b/Qt/ctrlmemview.cpp index c7c66374369d..5023d4f5f4a6 100644 --- a/Qt/ctrlmemview.cpp +++ b/Qt/ctrlmemview.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "EmuThread.h" #include "Core/MemMap.h" @@ -14,7 +15,6 @@ CtrlMemView::CtrlMemView(QWidget *parent) : QWidget(parent) { - curAddress=0; rowHeight=14; align=4; @@ -31,44 +31,30 @@ void CtrlMemView::redraw() update(); } - void CtrlMemView::wheelEvent(QWheelEvent* e) { int numDegrees = e->delta() / 8; int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Horizontal) { - } else { + if (e->orientation() == Qt::Vertical) + { curAddress -= numSteps*align*alignMul; - e->accept(); redraw(); - } + } } - void CtrlMemView::keyPressEvent(QKeyEvent *e) { int page=(rect().bottom()/rowHeight)/2-1; - if(e->key() == Qt::Key_Up) - { - curAddress-=align*alignMul; - e->accept(); - } - else if(e->key() == Qt::Key_Down) - { - curAddress+=align*alignMul; - e->accept(); - } - else if(e->key() == Qt::Key_PageUp) - { - curAddress-=page*align*alignMul; - e->accept(); - } - else if(e->key() == Qt::Key_PageDown) + switch (e->key()) { - curAddress+=page*align*alignMul; - e->accept(); + case Qt::Key_Up: curAddress -= align*alignMul; break; + case Qt::Key_Down: curAddress += align*alignMul; break; + case Qt::Key_PageUp: curAddress -= page*align*alignMul; break; + case Qt::Key_PageDown: curAddress += page*align*alignMul; break; + default: QWidget::keyPressEvent(e); break; } + redraw(); } @@ -85,17 +71,17 @@ void CtrlMemView::paintEvent(QPaintEvent *) int width = rect().width(); int numRows=(rect().bottom()/rowHeight)/2+1; - QPen nullPen=QPen(0xFFFFFF); - QPen currentPen=QPen(0xFF000000); - QPen selPen=QPen(0x808080); - QBrush lbr = QBrush(0xFFFFFF); - QBrush nullBrush=QBrush(0xFFFFFF); - QBrush currentBrush=QBrush(0xFFEfE8); - QBrush pcBrush=QBrush(0x70FF70); + QPen nullPen(0xFFFFFF); + QPen currentPen(0xFF000000); + QPen selPen(0x808080); + QBrush lbr(0xFFFFFF); + QBrush nullBrush(0xFFFFFF); + QBrush currentBrush(0xFFEFE8); + QBrush pcBrush(0x70FF70); QPen textPen; - QFont normalFont = QFont("Arial", 10); - QFont alignedFont = QFont("Monospace", 10); + QFont normalFont("Arial", 10); + QFont alignedFont("Monospace", 10); painter.setFont(normalFont); int i; @@ -111,7 +97,7 @@ void CtrlMemView::paintEvent(QPaintEvent *) painter.setBrush(currentBrush); - if (selecting && address == selection) + if (selecting && address == (unsigned int)selection) painter.setPen(selPen); else painter.setPen(i==0 ? currentPen : nullPen); @@ -187,6 +173,7 @@ void CtrlMemView::paintEvent(QPaintEvent *) painter.drawText(85,rowY1 - 2 + rowHeight, temp); break; } + case MV_MAX: break; } } } @@ -221,6 +208,10 @@ void CtrlMemView::contextMenu(const QPoint &pos) connect(copyValue, SIGNAL(triggered()), this, SLOT(CopyValue())); menu.addAction(copyValue); + QAction *changeValue = new QAction(tr("C&hange value"), this); + connect(changeValue, SIGNAL(triggered()), this, SLOT(Change())); + menu.addAction(changeValue); + QAction *dump = new QAction(tr("Dump..."), this); connect(dump, SIGNAL(triggered()), this, SLOT(Dump())); menu.addAction(dump); @@ -230,7 +221,9 @@ void CtrlMemView::contextMenu(const QPoint &pos) void CtrlMemView::CopyValue() { + EmuThread_LockDraw(true); QApplication::clipboard()->setText(QString("%1").arg(Memory::ReadUnchecked_U32(selection),8,16,QChar('0'))); + EmuThread_LockDraw(false); } void CtrlMemView::Dump() @@ -238,6 +231,27 @@ void CtrlMemView::Dump() QMessageBox::information(this,"Sorry","This feature has not been implemented.",QMessageBox::Ok); } + +void CtrlMemView::Change() +{ + EmuThread_LockDraw(true); + QString curVal = QString("%1").arg(Memory::ReadUnchecked_U32(selection),8,16,QChar('0')); + EmuThread_LockDraw(false); + + bool ok; + QString text = QInputDialog::getText(this, tr("Set new value"), + tr("Set new value:"), QLineEdit::Normal, + curVal, &ok); + if (ok && !text.isEmpty()) + { + EmuThread_LockDraw(true); + Memory::WriteUnchecked_U32(text.toUInt(0,16),selection); + EmuThread_LockDraw(false); + redraw(); + } +} + + int CtrlMemView::yToAddress(int y) { int ydiff=y-rect().bottom()/2-rowHeight/2; diff --git a/Qt/ctrlmemview.h b/Qt/ctrlmemview.h index 070b1af7f968..e4b52cfe6360 100644 --- a/Qt/ctrlmemview.h +++ b/Qt/ctrlmemview.h @@ -67,11 +67,11 @@ class CtrlMemView : public QWidget void keyPressEvent(QKeyEvent *e); void wheelEvent(QWheelEvent *e); void mousePressEvent(QMouseEvent *e); -signals: public slots: void CopyValue(); void Dump(); + void Change(); private: int curAddress; int align; diff --git a/Qt/ctrlregisterlist.cpp b/Qt/ctrlregisterlist.cpp index a52c5c88abb2..64c19644a0e1 100644 --- a/Qt/ctrlregisterlist.cpp +++ b/Qt/ctrlregisterlist.cpp @@ -68,26 +68,15 @@ void CtrlRegisterList::scrollChanged(int action) void CtrlRegisterList::keyPressEvent(QKeyEvent *e) { - if(e->key() == Qt::Key_Down) + switch (e->key()) { - selection += 1; - e->accept(); - } - else if(e->key() == Qt::Key_Up) - { - selection -= 1; - e->accept(); - } - else if(e->key() == Qt::Key_PageDown) - { - selection += 4; - e->accept(); - } - else if(e->key() == Qt::Key_PageUp) - { - selection -= 4; - e->accept(); + case Qt::Key_Down: selection += 1; break; + case Qt::Key_Up: selection -= 1; break; + case Qt::Key_PageDown: selection += 4; break; + case Qt::Key_PageUp: selection -= 4; break; + default: QWidget::keyPressEvent(e); break; } + int maxRowsDisplay =rect().bottom()/rowHeight - 1; curVertOffset = std::min(std::max(curVertOffset, selection-maxRowsDisplay),selection); update(); @@ -127,19 +116,17 @@ void CtrlRegisterList::mousePressEvent(QMouseEvent *e) { redraw(); } - e->accept(); } void CtrlRegisterList::wheelEvent(QWheelEvent* e) { int numDegrees = e->delta() / 8; int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Horizontal) { - } else { + if (e->orientation() == Qt::Vertical) + { curVertOffset -= numSteps; - e->accept(); update(); - } + } } @@ -175,17 +162,17 @@ void CtrlRegisterList::paintEvent(QPaintEvent *) int width = rect().width(); - QColor bgColor = QColor(0xffffff); - QPen nullPen=QPen(bgColor); - QPen currentPen=QPen(QColor(0xFF000000)); - QPen selPen=QPen(0x808080); + QColor bgColor(0xffffff); + QPen nullPen(bgColor); + QPen currentPen(QColor(0xFF000000)); + QPen selPen(0x808080); QPen textPen; QBrush lbr; lbr.setColor(bgColor); - QBrush nullBrush=QBrush(bgColor); - QBrush currentBrush=QBrush(0xFFEfE8); - QBrush pcBrush=QBrush(0x70FF70); + QBrush nullBrush(bgColor); + QBrush currentBrush(0xFFEfE8); + QBrush pcBrush(0x70FF70); int nc = cpu->GetNumCategories(); for (int i=0; iDisasmView->setAlign(cpu->getInstructionSize(0)); ui->DisasmView->redraw(); ui->RegList->redraw(); @@ -344,7 +346,7 @@ void Debugger_Disasm::FillFunctions() if(symbolMap.GetSymbolType(i) & ST_FUNCTION) { QListWidgetItem* item = new QListWidgetItem(); - item->setText(QString(symbolMap.GetSymbolName(i)) + " ("+ QString::number(symbolMap.GetSymbolSize(i)) +")"); + item->setText(QString("%1 (%2)").arg(symbolMap.GetSymbolName(i)).arg(symbolMap.GetSymbolSize(i))); item->setData(Qt::UserRole, symbolMap.GetAddress(i)); ui->FuncList->addItem(item); } @@ -429,7 +431,7 @@ void Debugger_Disasm::UpdateThreadGUI() std::vector threads = GetThreadsInfo(); EmuThread_LockDraw(false); - for(int i = 0; i < threads.size(); i++) + for(size_t i = 0; i < threads.size(); i++) { QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0,QString::number(threads[i].id)); @@ -478,7 +480,7 @@ void Debugger_Disasm::on_threadList_customContextMenuRequested(const QPoint &pos connect(gotoEntryPoint, SIGNAL(triggered()), this, SLOT(GotoThreadEntryPoint())); menu.addAction(gotoEntryPoint); - QMenu* changeStatus = menu.addMenu("Change status"); + QMenu* changeStatus = menu.addMenu(tr("Change status")); QAction *statusRunning = new QAction(tr("Running"), this); connect(statusRunning, SIGNAL(triggered()), this, SLOT(SetThreadStatusRun())); @@ -564,7 +566,7 @@ void Debugger_Disasm::UpdateDisplayListGUI() item->setText(3,QString("%1").arg(dl->pc,8,16,QChar('0'))); item->setData(3, Qt::UserRole, dl->pc); ui->displayList->addTopLevelItem(item); - if(curDlId == dl->id) + if(curDlId == (u32)dl->id) { ui->displayList->setCurrentItem(item); displayListRowSelected = item; @@ -593,7 +595,7 @@ void Debugger_Disasm::UpdateDisplayListGUI() item->setText(3,QString("%1").arg(it->pc,8,16,QChar('0'))); item->setData(3, Qt::UserRole, it->pc); ui->displayList->addTopLevelItem(item); - if(curDlId == it->id) + if(curDlId == (u32)it->id) { ui->displayList->setCurrentItem(item); displayListRowSelected = item; diff --git a/Qt/debugger_disasm.h b/Qt/debugger_disasm.h index 5ca646e4be27..4788b42fbf79 100644 --- a/Qt/debugger_disasm.h +++ b/Qt/debugger_disasm.h @@ -58,49 +58,31 @@ private slots: void UpdateThreadGUI(); void on_GotoPc_clicked(); - void on_Go_clicked(); - void on_Stop_clicked(); - void on_StepInto_clicked(); - void on_StepOver_clicked(); - void on_Skip_clicked(); - void on_NextHLE_clicked(); - void on_GotoLr_clicked(); - void on_GotoInt_currentIndexChanged(int index); - void on_Address_textChanged(const QString &arg1); - void on_DisasmView_customContextMenuRequested(const QPoint &pos); - void releaseLock(); void on_RegList_customContextMenuRequested(const QPoint &pos); - void on_vfpu_clicked(); - void on_FuncList_itemClicked(QListWidgetItem *item); - void on_breakpointsList_itemClicked(QTreeWidgetItem *item, int column); - void on_breakpointsList_customContextMenuRequested(const QPoint &pos); - void on_clearAllBP_clicked(); - void on_threadList_itemClicked(QTreeWidgetItem *item, int column); - void on_threadList_customContextMenuRequested(const QPoint &pos); void SetThreadStatusRun(); void SetThreadStatusWait(); void SetThreadStatusSuspend(); void on_displayList_customContextMenuRequested(const QPoint &pos); - + void releaseLock(); private: void SetThreadStatus(ThreadStatus status); diff --git a/Qt/debugger_displaylist.cpp b/Qt/debugger_displaylist.cpp index 0f557354ca93..ffbf6b004716 100644 --- a/Qt/debugger_displaylist.cpp +++ b/Qt/debugger_displaylist.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "Core/CPU.h" #include "ui_debugger_displaylist.h" @@ -12,7 +13,6 @@ #include "base/display.h" #include "mainwindow.h" #include "GPU/GLES/VertexDecoder.h" -#include Debugger_DisplayList::Debugger_DisplayList(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent) : @@ -30,6 +30,7 @@ Debugger_DisplayList::Debugger_DisplayList(DebugInterface *_cpu, MainWindow* mai QObject::connect(this, SIGNAL(updateDisplayList_()), this, SLOT(UpdateDisplayListGUI())); QObject::connect(this, SIGNAL(updateRenderBufferList_()), this, SLOT(UpdateRenderBufferListGUI())); + QObject::connect(this, SIGNAL(updateRenderBuffer_()), this, SLOT(UpdateRenderBufferGUI())); } @@ -76,39 +77,8 @@ void Debugger_DisplayList::UpdateDisplayListGUI() EmuThread_LockDraw(true); const std::deque& dlQueue = gpu->GetDisplayLists(); - DisplayList* dl = gpu->GetCurrentDisplayList(); - if(dl) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString::number(dl->id)); - item->setData(0, Qt::UserRole, dl->id); - switch(dl->status) - { - case PSP_GE_LIST_DONE: item->setText(1,"Done"); break; - case PSP_GE_LIST_QUEUED: item->setText(1,"Queued"); break; - case PSP_GE_LIST_DRAWING: item->setText(1,"Drawing"); break; - case PSP_GE_LIST_STALL_REACHED: item->setText(1,"Stall Reached"); break; - case PSP_GE_LIST_END_REACHED: item->setText(1,"End Reached"); break; - case PSP_GE_LIST_CANCEL_DONE: item->setText(1,"Cancel Done"); break; - default: break; - } - item->setText(2,QString("%1").arg(dl->startpc,8,16,QChar('0'))); - item->setData(2, Qt::UserRole, dl->startpc); - item->setText(3,QString("%1").arg(dl->pc,8,16,QChar('0'))); - item->setData(3, Qt::UserRole, dl->pc); - ui->displayList->addTopLevelItem(item); - if(curDlId == dl->id) - { - ui->displayList->setCurrentItem(item); - displayListRowSelected = item; - ShowDLCode(); - } - } - for(auto it = dlQueue.begin(); it != dlQueue.end(); ++it) { - if(dl && it->id == dl->id) - continue; QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0,QString::number(it->id)); item->setData(0, Qt::UserRole, it->id); @@ -127,7 +97,7 @@ void Debugger_DisplayList::UpdateDisplayListGUI() item->setText(3,QString("%1").arg(it->pc,8,16,QChar('0'))); item->setData(3, Qt::UserRole, it->pc); ui->displayList->addTopLevelItem(item); - if(curDlId == it->id) + if(curDlId == (u32)it->id) { ui->displayList->setCurrentItem(item); displayListRowSelected = item; @@ -136,6 +106,14 @@ void Debugger_DisplayList::UpdateDisplayListGUI() } for(int i = 0; i < ui->displayList->columnCount(); i++) ui->displayList->resizeColumnToContents(i); + + if (ui->displayList->selectedItems().size() == 0 && ui->displayList->topLevelItemCount() != 0) + { + ui->displayList->setCurrentItem(ui->displayList->topLevelItem(0)); + displayListRowSelected = ui->displayList->topLevelItem(0); + ShowDLCode(); + } + EmuThread_LockDraw(false); } @@ -169,7 +147,7 @@ void Debugger_DisplayList::ShowDLCode() item->setText(1,QString("%1").arg(it->second.cmd,2,16,QChar('0'))); item->setText(2,QString("%1").arg(it->second.data,6,16,QChar('0'))); item->setText(3,it->second.comment); - if(curPc == it->first) + if(curPc == (u32)it->first) { curTexAddr = it->second.texAddr; curVtxAddr = it->second.vtxAddr; @@ -185,7 +163,7 @@ void Debugger_DisplayList::ShowDLCode() } ui->displayListData->addTopLevelItem(item); - if(curPc == it->first) + if(curPc == (u32)it->first) { ui->displayListData->setCurrentItem(item); } @@ -207,7 +185,7 @@ void Debugger_DisplayList::ShowDLCode() // Textures QTreeWidgetItem* item = new QTreeWidgetItem(); u32 texaddr = (drawGPUState[i].texaddr[0] & 0xFFFFF0) | ((drawGPUState[i].texbufwidth[0]<<8) & 0x0F000000); - if(!(usedTexAddr.find(texaddr) != usedTexAddr.end() || !Memory::IsValidAddress(texaddr))) + if(usedTexAddr.find(texaddr) == usedTexAddr.end() && Memory::IsValidAddress(texaddr)) { u32 format = drawGPUState[i].texformat & 0xF; int w = 1 << (drawGPUState[i].texsize[0] & 0xf); @@ -232,7 +210,7 @@ void Debugger_DisplayList::ShowDLCode() QTreeWidgetItem* vertexItem = new QTreeWidgetItem(); u32 baseExtended = ((drawGPUState[i].base & 0x0F0000) << 8) | (drawGPUState[i].vaddr & 0xFFFFFF); u32 vaddr = ((drawGPUState[i].offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - if(!((drawGPUState[i].vaddr) == 0 || !Memory::IsValidAddress(vaddr) || usedVtxAddr.find(vaddr) != usedVtxAddr.end())) + if(drawGPUState[i].vaddr != 0 && Memory::IsValidAddress(vaddr) && usedVtxAddr.find(vaddr) == usedVtxAddr.end()) { vertexItem->setText(0, QString("%1").arg(vaddr,8,16,QChar('0'))); vertexItem->setData(0,Qt::UserRole, i); @@ -294,7 +272,7 @@ void Debugger_DisplayList::ShowDLCode() QTreeWidgetItem* indexItem = new QTreeWidgetItem(); baseExtended = ((drawGPUState[i].base & 0x0F0000) << 8) | (drawGPUState[i].iaddr & 0xFFFFFF); u32 iaddr = ((drawGPUState[i].offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - if(!((drawGPUState[i].iaddr & 0xFFFFFF) == 0 || !Memory::IsValidAddress(iaddr) || usedIdxAddr.find(iaddr) != usedIdxAddr.end())) + if((drawGPUState[i].iaddr & 0xFFFFFF) != 0 && Memory::IsValidAddress(iaddr) && usedIdxAddr.find(iaddr) == usedIdxAddr.end()) { indexItem->setText(0, QString("%1").arg(iaddr,8,16,QChar('0'))); indexItem->setData(0,Qt::UserRole, i); @@ -308,15 +286,16 @@ void Debugger_DisplayList::ShowDLCode() } usedIdxAddr.insert(iaddr); } - - for(int i = 0; i < ui->texturesList->columnCount(); i++) - ui->texturesList->resizeColumnToContents(i); - for(int i = 0; i < ui->vertexList->columnCount(); i++) - ui->vertexList->resizeColumnToContents(i); - for(int i = 0; i < ui->indexList->columnCount(); i++) - ui->indexList->resizeColumnToContents(i); } + + for(int i = 0; i < ui->texturesList->columnCount(); i++) + ui->texturesList->resizeColumnToContents(i); + for(int i = 0; i < ui->vertexList->columnCount(); i++) + ui->vertexList->resizeColumnToContents(i); + for(int i = 0; i < ui->indexList->columnCount(); i++) + ui->indexList->resizeColumnToContents(i); + UpdateVertexInfo(); UpdateIndexInfo(); } @@ -371,7 +350,7 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg { int bz_ucount = data & 0xFF; int bz_vcount = (data >> 8) & 0xFF; - return QString("DRAW BEZIER: %1 x %2").arg(bz_ucount).arg(bz_vcount); + return QString("DRAW BEZIER: U=%1 x V=%2").arg(bz_ucount).arg(bz_vcount); } break; @@ -379,9 +358,15 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg { int sp_ucount = data & 0xFF; int sp_vcount = (data >> 8) & 0xFF; + static const char* type[4] = { + "Close/Close", + "Open/Close", + "Close/Open", + "Open/Open" + }; int sp_utype = (data >> 16) & 0x3; int sp_vtype = (data >> 18) & 0x3; - return QString("DRAW SPLINE: %1 x %2, %3 x %4").arg(sp_ucount).arg(sp_vcount).arg(sp_utype).arg(sp_vtype); + return QString("DRAW SPLINE: U=%1 x V=%2, U Type = %3 , V Type = %4").arg(sp_ucount).arg(sp_vcount).arg(type[sp_utype]).arg(type[sp_vtype]); } break; @@ -458,13 +443,15 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_BJUMP: + { // bounding box jump. Let's just not jump, for now. - return QString("BBOX JUMP - unimplemented"); + u32 target = (((state.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; + return QString("BBOX JUMP - %1 to %2").arg(pc,8,16,QChar('0')).arg(target,8,16,QChar('0')); break; - + } case GE_CMD_BOUNDINGBOX: // bounding box test. Let's do nothing. - return QString("BBOX TEST - unimplemented"); + return QString("BBOX TEST - number : %1").arg(data & 0xFFFF,4,16,QChar('0')); break; case GE_CMD_ORIGIN: @@ -472,9 +459,57 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_VERTEXTYPE: - return QString("SetVertexType: %1").arg(data,6,16,QChar('0')); - break; + { + const char* format[4] = + { + "No", + "8 Bits fixed", + "16 Bits fixed", + "Float" + }; + const char* colFormat[8] = + { + "No", + "", + "", + "", + "16-bit BGR-5650", + "16-bit ABGR-5551", + "16-bit ABGR-4444", + "32-bit ABGR-8888" + }; + QString retString = "SetVertexType:"; + + u32 transform = data & GE_VTYPE_THROUGH_MASK; + retString += QString(" Transform : %1").arg(transform==0?"Transformed":"Raw"); + + u32 numVertMorph = (data & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT; + retString += QString(", Num Vtx Morph : %1").arg(numVertMorph); + + u32 numWeight = (data & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT; + retString += QString(", Num Weights : %1").arg(numWeight); + + u32 indexFmt = (data & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT; + retString += QString(", Index Format : %1").arg(format[indexFmt]); + u32 weightFmt = (data & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT; + retString += QString(", Weight Format : %1").arg(format[weightFmt]); + + u32 posFmt = (data & GE_VTYPE_POS_MASK) >> GE_VTYPE_POS_SHIFT; + retString += QString(", Position Format : %1").arg(format[posFmt]); + + u32 nrmFmt = (data & GE_VTYPE_NRM_MASK) >> GE_VTYPE_NRM_SHIFT; + retString += QString(", Normal Format : %1").arg(format[nrmFmt]); + + u32 colFmt = (data & GE_VTYPE_COL_MASK) >> GE_VTYPE_COL_SHIFT; + retString += QString(", Color Format : %1").arg(colFormat[colFmt]); + + u32 tcFmt = (data & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT; + retString += QString(", Texture UV Format : %1").arg(format[tcFmt]); + + return retString; + break; + } case GE_CMD_OFFSETADDR: return QString("OffsetAddr: %1").arg(data,6,16,QChar('0')); break; @@ -587,9 +622,17 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_FRAMEBUFPIXFORMAT: - return QString("FramebufPixeFormat: %1").arg(data); + { + const char* fmt[4] = + { + "16-bit BGR 5650", + "16-bit ABGR 5551", + "16-bit ABGR 4444", + "32-bit ABGR 8888" + }; + return QString("FramebufPixeFormat: %1").arg(fmt[data]); break; - + } case GE_CMD_TEXADDR0: case GE_CMD_TEXADDR1: case GE_CMD_TEXADDR2: @@ -609,7 +652,7 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg case GE_CMD_TEXBUFWIDTH5: case GE_CMD_TEXBUFWIDTH6: case GE_CMD_TEXBUFWIDTH7: - return QString("Texture BUFWIDTHess %1: %2").arg(cmd-GE_CMD_TEXBUFWIDTH0).arg(data,6,16,QChar('0')); + return QString("Texture BUFWIDTHess %1: %2 width : %3").arg(cmd-GE_CMD_TEXBUFWIDTH0).arg(data,6,16,QChar('0')).arg(data & 0xFFFF); break; case GE_CMD_CLUTADDR: @@ -622,20 +665,41 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg case GE_CMD_LOADCLUT: // This could be used to "dirty" textures with clut. - return QString("Clut load"); + return QString("Clut load, numColor : %1").arg(data*8); break; case GE_CMD_TEXMAPMODE: - return QString("Tex map mode: %1").arg(data,6,16,QChar('0')); + { + const char* texMapMode[3] = + { + "Texture Coordinates (UV)", + "Texture Matrix", + "Environment Map" + }; + const char* texProjMode[4] = + { + "Position", + "Texture Coordinates", + "Normalized Normal", + "Normal" + }; + return QString("Tex map mode: Map mode : %1, Proj Mode : %2").arg(texMapMode[data & 0x3]).arg(texProjMode[(data >> 8) & 0x3]); break; - + } case GE_CMD_TEXSHADELS: return QString("Tex shade light sources: %1").arg(data,6,16,QChar('0')); break; case GE_CMD_CLUTFORMAT: { - return QString("Clut format: %1").arg(data,6,16,QChar('0')); + const char* fmt[4] = + { + "16-bit BGR 5650", + "16-bit ABGR 5551", + "16-bit ABGR 4444", + "32-bit ABGR 8888" + }; + return QString("Clut format: %1 , Fmt : %2").arg(data,6,16,QChar('0')).arg(fmt[data & 0x3]); } break; @@ -695,7 +759,7 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg case GE_CMD_TRANSFERSTART: // Orphis calls this TRXKICK { - return QString("Block Transfer Start"); + return QString("Block Transfer Start : %1").arg(data ? "32-bit texel size" : "16-bit texel size"); break; } @@ -771,9 +835,22 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg case GE_CMD_LIGHTTYPE1: case GE_CMD_LIGHTTYPE2: case GE_CMD_LIGHTTYPE3: - return QString("Light %1 type: %2").arg(cmd-GE_CMD_LIGHTTYPE0).arg(data,6,16,QChar('0')); + { + const char* lightType[3] = + { + "Directional Light", + "Point Light", + "Spot Light" + }; + const char* lightComp[3] = + { + "Ambient & Diffuse", + "Diffuse & Specular", + "Unknown (diffuse color, affected by specular power)" + }; + return QString("Light %1 type: %2 %3").arg(cmd-GE_CMD_LIGHTTYPE0).arg(lightType[(data) >> 8 & 0x3]).arg(lightComp[data & 0x3]); break; - + } case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: case GE_CMD_LX1:case GE_CMD_LY1:case GE_CMD_LZ1: case GE_CMD_LX2:case GE_CMD_LY2:case GE_CMD_LZ2: @@ -854,42 +931,78 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_CULL: - return QString("cull: %1").arg(data,6,16,QChar('0')); + { + const char* cull[2] = + { + "Clockwise visible", + "Counter-clockwise visible" + }; + return QString("cull: %1").arg(cull[data & 0x1]); break; - + } case GE_CMD_PATCHDIVISION: { int patch_div_s = data & 0xFF; int patch_div_t = (data >> 8) & 0xFF; - return QString("Patch subdivision: %1 x %2").arg(patch_div_s).arg(patch_div_t); + return QString("Patch subdivision: S=%1 x T=%2").arg(patch_div_s).arg(patch_div_t); } break; case GE_CMD_PATCHPRIMITIVE: - return QString("Patch Primitive: %1").arg(data); + { + const char* type[3] = + { + "Triangles", + "Lines", + "Points" + }; + return QString("Patch Primitive: %1").arg(type[data]); break; - + } case GE_CMD_PATCHFACING: - return QString( "Patch Facing: %1").arg(data); + { + const char* val[2] = + { + "Clockwise", + "Counter-Clockwise" + }; + return QString( "Patch Facing: %1").arg(val[data]); break; - + } case GE_CMD_REVERSENORMAL: return QString("Reverse normal: %1").arg(data); break; case GE_CMD_MATERIALUPDATE: - return QString("Material Update: %1").arg(data); + { + QString txt = ""; + if(data & 1) txt += " Ambient"; + if(data & 2) txt += " Diffuse"; + if(data & 4) txt += " Specular"; + return QString("Material Update: %1").arg(txt); break; - + } ////////////////////////////////////////////////////////////////// // CLEARING ////////////////////////////////////////////////////////////////// case GE_CMD_CLEARMODE: + { // If it becomes a performance problem, check diff&1 - return QString("Clear mode: %1").arg(data,6,16,QChar('0')); + const char* clearMode[8] = + { + "", + "Clear Color Buffer", + "Clear Stencil/Alpha Buffer", + "", + "Clear Depth Buffer", + "", + "", + "" + }; + return QString("Clear mode: %1, enabled : %2").arg(clearMode[(data >> 8) & 0xF]).arg(data & 0x1); break; - + } ////////////////////////////////////////////////////////////////// // ALPHA BLENDING @@ -899,9 +1012,31 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_BLENDMODE: - return QString("Blend mode: %1").arg(data,6,16,QChar('0')); + { + const char* func[9] = + { + "Source Color", + "One Minus Source Color", + "Source Alpha", + "One Minus Source Alpha", + "Destination Color", + "One Minus Destination Color", + "Destination Alpha", + "One Minus Destination Alpha", + "Fix" + }; + const char* op[6] = + { + "Add", + "Subtract", + "Reverse Subtract", + "Minimum Value", + "Maximum Value", + "Absolute Value" + }; + return QString("Blend mode: Src : %1, Dest : %2, Op : %3").arg(func[(data >> 4) & 0xF]).arg(func[(data >> 8) & 0xF]).arg(op[(data) & 0x7]); break; - + } case GE_CMD_BLENDFIXEDA: return QString("Blend fix A: %1").arg(data,6,16,QChar('0')); break; @@ -915,9 +1050,21 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_ALPHATEST: - return QString("Alpha test settings"); + { + const char* testFunc[8] = + { + "Never pass pixel", + "Always pass pixel", + "Pass pixel if match", + "Pass pixel if difference", + "Pass pixel if less", + "Pass pixel if less or equal", + "Pass pixel if greater", + "Pass pixel if greater or equal" + }; + return QString("Alpha test settings, Mask : %1, Ref : %2, Test : %3").arg((data >> 8) & 0xFF).arg((data >> 16) & 0xFF).arg(testFunc[data & 0x7]); break; - + } case GE_CMD_ANTIALIASENABLE: return QString("Antialias enable: %1").arg(data); break; @@ -927,22 +1074,71 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_COLORTESTENABLE: - return QString("Color Test enable: %1").arg(data); + { + const char* colorTest[4] = + { + "Never pass pixel", + "Always pass pixel", + "Pass pixel if color matches", + "Pass pixel if color differs" + }; + return QString("Color Test enable: %1").arg(colorTest[data]); break; - + } case GE_CMD_LOGICOPENABLE: - return QString("Logic op enable: %1").arg(data); + { + const char* logicOp[16] = + { + "Clear", + "And", + "Reverse And", + "Copy", + "Inverted And", + "No Operation", + "Exclusive Or", + "Or", + "Negated Or", + "Equivalence", + "Inverted", + "Reverse Or", + "Inverted Copy", + "Inverted Or", + "Negated And", + "Set" + }; + return QString("Logic op enable: %1").arg(logicOp[data]); break; - + } case GE_CMD_TEXFUNC: - return QString("TexFunc %1").arg(data&7); + { + const char* effect[8] = + { + "Modulate", + "Decal", + "Blend", + "Replace", + "Add", + "","","" + }; + return QString("TexFunc %1 / %2 / %3").arg((data&0x100)?"Texture alpha is read":"Texture alpha is ignored").arg((data & 0x10000)?"Fragment color is doubled":"Fragment color is untouched").arg(effect[data & 0x7]); break; - + } case GE_CMD_TEXFILTER: { - int min = data & 7; - int mag = (data >> 8) & 1; - return QString("TexFilter min: %1 mag: %2").arg( min).arg(mag); + const char* filter[8]= + { + "Nearest", + "Linear", + "", + "", + "Nearest; Mipmap Nearest", + "Linear; Mipmap Nearest", + "Nearest; Mipmap Linear", + "Linear; Mipmap Linear" + }; + int min = data & 0x7; + int mag = (data >> 8) & 0x7; + return QString("TexFilter min: %1 mag: %2").arg(filter[min]).arg(filter[mag]); } break; @@ -951,13 +1147,30 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_TEXMODE: - return QString("TexMode %1").arg(data,8,16,QChar('0')); + { + u32 maxMipMap = (data >> 16) & 0xF; + return QString("TexMode MaxmipMap : %1, Swizzle : %2").arg(maxMipMap).arg(data & 0x1); break; - + } case GE_CMD_TEXFORMAT: - return QString("TexFormat %1").arg(data,8,16,QChar('0')); + { + const char* texFmt[11] = + { + "16-bit BGR 5650", + "16-bit ABGR 5551", + "16-bit ABGR 4444", + "32-bit ABGR 8888", + "4-bit indexed", + "8-bit indexed", + "16-bit indexed", + "32-bit indexed", + "DXT1", + "DXT3", + "DXT5" + }; + return QString("TexFormat %1").arg(texFmt[data]); break; - + } case GE_CMD_TEXFLUSH: return QString("TexFlush"); break; @@ -967,9 +1180,15 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_TEXWRAP: - return QString("TexWrap %1").arg(data,8,16,QChar('0')); + { + const char* wrapMode[2] = + { + "Repeat", + "Clamp" + }; + return QString("TexWrap U : %1, V : %2").arg(wrapMode[data & 0x1]).arg(wrapMode[(data >> 8) & 0x1]); break; - + } case GE_CMD_TEXLEVEL: return QString("TexWrap Mode: %1 Offset: %2").arg(data&3).arg(data >> 16); break; @@ -999,21 +1218,57 @@ QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUg break; case GE_CMD_STENCILOP: - return QString("Stencil op: %1").arg(data,6,16,QChar('0')); + { + const char* stencilOp[8] = + { + "Keep stencil value", + "Zero stencil value", + "Replace stencil value", + "Invert stencil value", + "Increment stencil value", + "Decrement stencil value", + "","" + }; + return QString("Stencil op: ZFail : %1, Fail : %2, Pass : %3").arg(stencilOp[(data >> 16) & 0x7]).arg(stencilOp[(data >> 8) & 0x7]).arg(stencilOp[(data) & 0x7]); break; - + } case GE_CMD_STENCILTEST: - return QString("Stencil test: %1").arg(data,6,16,QChar('0')); + { + const char* testFunc[8] = + { + "Never pass stencil pixel", + "Always pass stencil pixel", + "Pass test if match", + "Pass test if difference", + "Pass test if less", + "Pass test if less or equal", + "Pass test if greater", + "Pass test if greater or equal" + }; + return QString("Stencil test, Mask : %1, Ref : %2, Test : %3").arg((data >> 8) & 0xFF).arg((data >> 16) & 0xFF).arg(testFunc[data & 0x7]); break; + } case GE_CMD_STENCILTESTENABLE: return QString("Stencil test enable: %1").arg(data); break; case GE_CMD_ZTEST: - return QString("Z test mode: %1").arg(data); + { + const char* testFunc[8] = + { + "Never pass stencil pixel", + "Always pass stencil pixel", + "Pass pixel if match", + "Pass pixel if difference", + "Pass pixel if less", + "Pass pixel if less or equal", + "Pass pixel if greater", + "Pass pixel if greater or equal" + }; + return QString("Z test mode: %1").arg(testFunc[data & 0x7]); break; - + } case GE_CMD_MORPHWEIGHT0: case GE_CMD_MORPHWEIGHT1: case GE_CMD_MORPHWEIGHT2: @@ -1188,6 +1443,8 @@ void Debugger_DisplayList::FillDisplayListCmd(std::map& data, u32 void Debugger_DisplayList::Update() { + if(!isVisible()) + return; UpdateRenderBuffer(); UpdateRenderBufferList(); UpdateDisplayList(); @@ -1220,6 +1477,11 @@ void Debugger_DisplayList::on_stopBtn_clicked() } void Debugger_DisplayList::UpdateRenderBuffer() +{ + emit updateRenderBuffer_(); +} + +void Debugger_DisplayList::UpdateRenderBufferGUI() { EmuThread_LockDraw(true); @@ -1287,7 +1549,7 @@ void Debugger_DisplayList::on_gotoPCBtn_clicked() for(int i = 0; i < ui->displayListData->topLevelItemCount(); i++) { - if(ui->displayListData->topLevelItem(i)->data(0, Qt::UserRole).toInt() == currentPC) + if((u32)ui->displayListData->topLevelItem(i)->data(0, Qt::UserRole).toInt() == currentPC) { ui->displayListData->setCurrentItem(ui->displayListData->topLevelItem(i)); } @@ -1302,7 +1564,7 @@ void Debugger_DisplayList::on_texturesList_itemDoubleClicked(QTreeWidgetItem *it void Debugger_DisplayList::on_comboBox_currentIndexChanged(int index) { currentRenderFrameDisplay = index; - UpdateRenderBuffer(); + UpdateRenderBufferGUI(); } void Debugger_DisplayList::UpdateRenderBufferList() @@ -1324,7 +1586,7 @@ void Debugger_DisplayList::UpdateRenderBufferListGUI() std::vector fboList = gpu->GetFramebufferList(); - for(int i = 0; i < fboList.size(); i++) + for(size_t i = 0; i < fboList.size(); i++) { QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0,QString("%1").arg(fboList[i].fb_address,8,16,QChar('0'))); @@ -1344,7 +1606,7 @@ void Debugger_DisplayList::on_fboList_itemClicked(QTreeWidgetItem *item, int col u64 addr = item->data(0,Qt::UserRole).toULongLong(); FBO* fbo = (FBO*)addr; currentTextureDisplay = fbo; - UpdateRenderBuffer(); + UpdateRenderBufferGUI(); } void Debugger_DisplayList::on_nextDLBtn_clicked() @@ -1357,7 +1619,7 @@ void Debugger_DisplayList::setCurrentFBO(u32 addr) { for(int i = 0; i < ui->fboList->topLevelItemCount(); i++) { - if(ui->fboList->topLevelItem(i)->data(0,Qt::UserRole+1).toInt() == addr) + if((u32)ui->fboList->topLevelItem(i)->data(0,Qt::UserRole+1).toInt() == addr) { for(int j = 0; j < ui->fboList->colorCount(); j++) ui->fboList->topLevelItem(i)->setTextColor(j,Qt::green); @@ -1402,7 +1664,7 @@ void Debugger_DisplayList::UpdateVertexInfo() VertexDecoder vtcDec; vtcDec.SetVertexType(state.vertType); - u8 tmp[20*vtcDec.GetDecVtxFmt().stride]; + u8* tmp = new u8[20*vtcDec.GetDecVtxFmt().stride]; vtcDec.DecodeVerts(tmp,Memory::GetPointer(vaddr),0,0,0,0,19); VertexReader vtxRead(tmp,vtcDec.GetDecVtxFmt(),state.vertType); @@ -1454,6 +1716,7 @@ void Debugger_DisplayList::UpdateVertexInfo() item->setText(2,QString("X: %1, Y: %2, Z: %3").arg(pos[0]).arg(pos[1]).arg(pos[2])); itemTop->addChild(item); } + delete [] tmp; for(int i = 0; i < ui->vertexData->columnCount(); i++) { ui->vertexData->resizeColumnToContents(i); @@ -1518,3 +1781,49 @@ void Debugger_DisplayList::on_indexList_itemClicked(QTreeWidgetItem *item, int c { UpdateIndexInfo(); } + +void Debugger_DisplayList::on_displayListData_customContextMenuRequested(const QPoint &pos) +{ + QTreeWidgetItem* item = ui->displayListData->itemAt(pos); + if(!item) + return; + displayListDataSelected = item; + + QMenu menu(this); + + QAction *runToHere = new QAction(tr("Run to here"), this); + connect(runToHere, SIGNAL(triggered()), this, SLOT(RunToDLPC())); + menu.addAction(runToHere); + + menu.exec( ui->displayListData->mapToGlobal(pos)); +} + +void Debugger_DisplayList::RunToDLPC() +{ + u32 addr = displayListDataSelected->text(0).toUInt(0,16); + host->SetGPUStep(true, 2, addr); + host->NextGPUStep(); +} + +void Debugger_DisplayList::on_texturesList_customContextMenuRequested(const QPoint &pos) +{ + QTreeWidgetItem* item = ui->texturesList->itemAt(pos); + if(!item) + return; + textureDataSelected = item; + + QMenu menu(this); + + QAction *runToDraw = new QAction(tr("Run to draw using this texture"), this); + connect(runToDraw, SIGNAL(triggered()), this, SLOT(RunToDrawTex())); + menu.addAction(runToDraw); + + menu.exec( ui->texturesList->mapToGlobal(pos)); +} + +void Debugger_DisplayList::RunToDrawTex() +{ + u32 addr = textureDataSelected->text(0).toUInt(0,16); + host->SetGPUStep(true, 3, addr); + host->NextGPUStep(); +} diff --git a/Qt/debugger_displaylist.h b/Qt/debugger_displaylist.h index e237a84f311e..78b19d86b45a 100644 --- a/Qt/debugger_displaylist.h +++ b/Qt/debugger_displaylist.h @@ -54,44 +54,35 @@ class Debugger_DisplayList : public QDialog signals: void updateDisplayList_(); void updateRenderBufferList_(); + void updateRenderBuffer_(); private slots: void UpdateDisplayListGUI(); void UpdateRenderBufferListGUI(); + void UpdateRenderBufferGUI(); void releaseLock(); void on_displayList_itemClicked(QTreeWidgetItem *item, int column); - void on_stepBtn_clicked(); - void on_runBtn_clicked(); - void on_stopBtn_clicked(); - void on_nextDrawBtn_clicked(); - void on_gotoPCBtn_clicked(); - void on_texturesList_itemDoubleClicked(QTreeWidgetItem *item, int column); - void on_comboBox_currentIndexChanged(int index); - void on_fboList_itemClicked(QTreeWidgetItem *item, int column); - void on_nextDLBtn_clicked(); void setCurrentFBO(u32 addr); - void on_zoommBtn_clicked(); - void on_zoompBtn_clicked(); - void on_vertexList_itemClicked(QTreeWidgetItem *item, int column); - void on_pushButton_clicked(); - void on_nextIdx_clicked(); - void on_indexList_itemClicked(QTreeWidgetItem *item, int column); + void on_displayListData_customContextMenuRequested(const QPoint &pos); + void on_texturesList_customContextMenuRequested(const QPoint &pos); + void RunToDLPC(); + void RunToDrawTex(); private: QString DisassembleOp(u32 pc, u32 op, u32 prev, const GPUgstate &state); @@ -100,6 +91,8 @@ private slots: DebugInterface* cpu; MainWindow* mainWindow; QTreeWidgetItem* displayListRowSelected; + QTreeWidgetItem* displayListDataSelected; + QTreeWidgetItem* textureDataSelected; int currentRenderFrameDisplay; FBO* currentTextureDisplay; float fboZoomFactor; diff --git a/Qt/debugger_displaylist.ui b/Qt/debugger_displaylist.ui index 77a3ee5415b2..894a479ad2dd 100644 --- a/Qt/debugger_displaylist.ui +++ b/Qt/debugger_displaylist.ui @@ -118,6 +118,9 @@ + + Qt::CustomContextMenu + false @@ -192,6 +195,9 @@ + + Qt::CustomContextMenu + Address diff --git a/Qt/debugger_memory.cpp b/Qt/debugger_memory.cpp index 3b5cdedf08d8..d044145f5d24 100644 --- a/Qt/debugger_memory.cpp +++ b/Qt/debugger_memory.cpp @@ -51,7 +51,7 @@ void Debugger_Memory::Goto(u32 addr) void Debugger_Memory::on_editAddress_textChanged(const QString &arg1) { - ui->memView->gotoAddr(arg1.toInt(0,16) & ~3); + ui->memView->gotoAddr(arg1.toUInt(0,16) & ~3); } void Debugger_Memory::on_normalBtn_clicked() diff --git a/Qt/debugger_memorytex.cpp b/Qt/debugger_memorytex.cpp index bc9ea6272d0a..e3fbc38285bf 100644 --- a/Qt/debugger_memorytex.cpp +++ b/Qt/debugger_memorytex.cpp @@ -61,17 +61,19 @@ void Debugger_MemoryTex::on_readBtn_clicked() EmuThread_LockDraw(true); GPUgstate state; - state.texaddr[0] = ui->texaddr->text().toInt(0,16); - state.texbufwidth[0] = ui->texbufwidth0->text().toInt(0,16); - state.texformat = ui->texformat->text().toInt(0,16); - state.texsize[0] = ui->texsize->text().toInt(0,16); - state.texmode = ui->texmode->text().toInt(0,16); - state.clutformat = ui->clutformat->text().toInt(0,16); - state.clutaddr = ui->clutaddr->text().toInt(0,16); - state.clutaddrupper = ui->clutaddrupper->text().toInt(0,16); - state.loadclut = ui->loadclut->text().toInt(0,16); + state.texaddr[0] = ui->texaddr->text().toUInt(0,16); + state.texbufwidth[0] = ui->texbufwidth0->text().toUInt(0,16); + state.texformat = ui->texformat->text().toUInt(0,16); + state.texsize[0] = ui->texsize->text().toUInt(0,16); + state.texmode = ui->texmode->text().toUInt(0,16); + state.clutformat = ui->clutformat->text().toUInt(0,16); + state.clutaddr = ui->clutaddr->text().toUInt(0,16); + state.clutaddrupper = ui->clutaddrupper->text().toUInt(0,16); + state.loadclut = ui->loadclut->text().toUInt(0,16); + int bufW = state.texbufwidth[0] & 0x3ff; int w = 1 << (state.texsize[0] & 0xf); int h = 1 << ((state.texsize[0]>>8) & 0xf); + w = std::max(bufW,w); uchar* newData = new uchar[w*h*4]; if(gpu->DecodeTexture(newData, state)) diff --git a/Qt/debugger_vfpu.cpp b/Qt/debugger_vfpu.cpp index 5c30f3e12f1a..290dd26f08a6 100644 --- a/Qt/debugger_vfpu.cpp +++ b/Qt/debugger_vfpu.cpp @@ -12,7 +12,7 @@ Debugger_VFPU::Debugger_VFPU(DebugInterface *_cpu, MainWindow* mainWindow_, QWid { ui->setupUi(this); - setWindowTitle(QString("VFPU - ")+cpu->GetName()); + setWindowTitle(QString("VFPU - %1").arg(cpu->GetName())); ui->vfpu->setCPU(_cpu); } diff --git a/Qt/gamepaddialog.cpp b/Qt/gamepaddialog.cpp index 5094fc032c5d..2b1fb03e0393 100644 --- a/Qt/gamepaddialog.cpp +++ b/Qt/gamepaddialog.cpp @@ -44,10 +44,10 @@ const int configOffset = 200; GamePadDialog::GamePadDialog(InputState* state, QWidget *parent) : QDialog(parent), ui(new Ui::GamePadDialog), - m_inputState(state), #if QT_HAS_SDL m_joystick(0), #endif + m_inputState(state), m_isInit(false) { ui->setupUi(this); @@ -108,15 +108,10 @@ void GamePadDialog::showEvent(QShowEvent *) void GamePadDialog::changeEvent(QEvent *event) { - QDialog::changeEvent(event); - - if (0 != event) + if (event->type() == QEvent::LanguageChange) { - if (event->type() == QEvent::LanguageChange) - { - ui->retranslateUi(this); - on_refreshListBtn_clicked(); - } + ui->retranslateUi(this); + on_refreshListBtn_clicked(); } } diff --git a/Qt/gamepaddialog.h b/Qt/gamepaddialog.h index 24163731ccbd..4157fa2aa5a2 100644 --- a/Qt/gamepaddialog.h +++ b/Qt/gamepaddialog.h @@ -21,23 +21,16 @@ class GamePadDialog : public QDialog ~GamePadDialog(); void SetViewMode(); - void SetCalibMode(); - void CalibNextButton(); protected: void showEvent(QShowEvent *); void changeEvent(QEvent *); private slots: void releaseLock(); void on_refreshListBtn_clicked(); - void on_SelectPadBtn_clicked(); - void pollJoystick(); - void on_AssignBtn_clicked(); - void on_buttonBox_accepted(); - private: int GetIntFromMapping(int inputId, int type, int sign); void GetMappingFromInt(int value, int &inputId, int &type, int &sign); diff --git a/Qt/git-version-gen.sh b/Qt/git-version-gen.sh new file mode 100755 index 000000000000..d441c38379a3 --- /dev/null +++ b/Qt/git-version-gen.sh @@ -0,0 +1,52 @@ +#!/bin/bash +## Copyright (c) 2012- PPSSPP Project. + +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, version 2.0 or later versions. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License 2.0 for more details. + +## A copy of the GPL 2.0 should have been included with the program. +## If not, see http://www.gnu.org/licenses/ + +## Official git repository and contact information can be found at +## https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +GIT_VERSION_FILE=$(dirname $0)/../git-version.cpp + +if [ -e "$GIT_VERSION_FILE" ]; then + # Skip updating the file if PPSSPP_GIT_VERSION_NO_UPDATE is 1. + results=$(grep '^#define PPSSPP_GIT_VERSION_NO_UPDATE 1' "$GIT_VERSION_FILE") + if [ "$results" != "" ]; then + exit 0 + fi +fi + +GIT_VERSION=$(git describe --always) +if [ "$GIT_VERSION" == "" ]; then + echo "Unable to update git-version.cpp, git not on path." 1>&2 + + echo "// This is a generated file." > "$GIT_VERSION_FILE" + echo >> "$GIT_VERSION_FILE" + echo 'const char *PPSSPP_GIT_VERSION = "unknown";' >> "$GIT_VERSION_FILE" + exit 0 +fi + +# Don't modify the file if it already has the current version. +if [ -e "$GIT_VERSION_FILE" ]; then + results=$(grep "$GIT_VERSION" "$GIT_VERSION_FILE") + if [ "$results" != "" ]; then + exit 0 + fi +fi + +echo "// This is a generated file." > "$GIT_VERSION_FILE" +echo >> "$GIT_VERSION_FILE" +echo 'const char *PPSSPP_GIT_VERSION = "'"$GIT_VERSION"'";' >> "$GIT_VERSION_FILE" +echo >> "$GIT_VERSION_FILE" +echo "// If you don't want this file to update/recompile, change to 1." >> "$GIT_VERSION_FILE" +echo "#define PPSSPP_GIT_VERSION_NO_UPDATE 0" >> "$GIT_VERSION_FILE" diff --git a/Qt/languages/ppsspp_cn.ts b/Qt/languages/ppsspp_cn.ts index 7e628dd6f7c4..0185206b1a8a 100644 --- a/Qt/languages/ppsspp_cn.ts +++ b/Qt/languages/ppsspp_cn.ts @@ -1,13 +1,13 @@ - + Controls Controls Controls window title - 控制器 + 控制器 @@ -65,7 +65,7 @@ New function name: - + 新函数å称: @@ -116,7 +116,7 @@ Set new value: - + 设置新值: @@ -125,7 +125,7 @@ Disassembler Window title - + å汇编器 @@ -330,7 +330,7 @@ Memory Viewer - %1 - + 内存查看器 - %1 @@ -338,7 +338,7 @@ VFPU - + VFPU @@ -527,37 +527,37 @@ Lo&g Levels - + (&G)日志级别 &Language - + (&L)语言选择 &Video - + (&V)视频 &Anisotropic filtering - + (&A)å„å‘异性过滤 &Zoom - + (&Z)缩放 Co&ntrols - + (&N)控制 &Core - + (&C)核心 @@ -702,47 +702,47 @@ &Keyboard - + (&K)键盘 &Toggle fullscreen - + (&T)å…¨å± Show &debug statistics - + (&D)æ˜¾ç¤ºè°ƒè¯•ä¿¡æ¯ I&gnore illegal reads/writes - + (&G)忽略éžæ³•è¯»å†™ &Gamepad - + (&G)手柄 Run on loa&d - + (&D)载入时è¿è¡Œ Show &FPS counter - + (&F)显示帧率计数器 S&tretch to display - + (&T)拉伸显示 &Sound emulation - + (&S)模拟声音 @@ -895,27 +895,27 @@ Off - + 关闭 2x - + 2x 4x - + 4x 8x - + 8x 16x - + 16x diff --git a/Qt/languages/ppsspp_fr.ts b/Qt/languages/ppsspp_fr.ts index ce5006357e81..093fd8597558 100644 --- a/Qt/languages/ppsspp_fr.ts +++ b/Qt/languages/ppsspp_fr.ts @@ -7,7 +7,7 @@ Controls Controls window title - + Contrôles @@ -376,22 +376,22 @@ Gamepad Configuration - + Configuration du pad GamePad List - + Liste des controlleurs de jeu Refresh - + Rafraîchir Select - + Sélectionner @@ -406,22 +406,22 @@ Assign Gamepad input - + Assigner la touche du pad to PSP button/axis - + au boutton PSP Assign - + Assigner Press buttons on your gamePad to verify mapping : - + Vérifier les contrôles en appuyant sur les bouttons du pad : @@ -511,12 +511,12 @@ G3D - + G3D HLE - + HLE @@ -526,37 +526,37 @@ Lo&g Levels - + Op&tions du journal &Language - + &Langue &Video - + &Video &Anisotropic filtering - + &Filtrage anisotrope &Zoom - + &Zoom Co&ntrols - + Co&ntrôles &Core - + &Emulation @@ -576,37 +576,37 @@ - - + - Quickload state - + Chargement rapide F4 - + F4 Quicksave state - + Sauvegarde rapide F2 - + F2 &Load State File... - + &Charger... &Save State File... - + &Sauvegarder... @@ -616,42 +616,43 @@ &Run - + &Démarrer F7 - + F7 &Pause - + &Pause F8 - + F8 R&eset - + R&eset &Interpreter - + &Interpreteur &Slightly Faster Interpreter - + Could delete the "un peu" which means slightly but takes a lot of space + &Interpreteur un peu plus rapide &Dynarec - + &DRC @@ -676,17 +677,17 @@ Ctrl+D - + Ctrl+D &Log Console - + &Journal d'événements Ctrl+L - + Ctrl+L @@ -696,87 +697,87 @@ Ctrl+M - + Ctrl+M &Keyboard - + &Clavier &Toggle fullscreen - + &Plein écran Show &debug statistics - + Afficher &les statistiques de débogage I&gnore illegal reads/writes - + I&gnorer les écritures/lectures illégales &Gamepad - + &Pad Run on loa&d - + Lancer au cha&rgement Show &FPS counter - + Afficher &les FPS S&tretch to display - + A&dapter à l'écran &Sound emulation - + &Son F12 - + F12 &Buffered Rendering - + &Rendu Tampon F5 - + F5 &Hardware Transform - + &Hardware Transform F6 - + F6 &Linear Filtering - + &Filtrage Linéaire &Wireframe (experimental) - + &Rendu en fil de fer (expérimental) @@ -786,90 +787,90 @@ Screen &1x - + &1x Ctrl+1 - + Ctrl+1 Screen &2x - + &2x Ctrl+2 - + Ctrl+2 Screen &3x - + &3x Ctrl+3 - + Ctrl+3 Screen &4x - + &4x Ctrl+4 - + Ctrl+4 &Fast Memory (dynarec, unstable) - + &Mémoire Rapide (DRC, instable) &Go to http://www.ppsspp.org/ - + &Aller à http://www.ppsspp.org/ &About PPSSPP... - + &A propos de PPSSPP... &Use VBO - + &Utiliser les VBO Debug - + Débogage Warning - + Avertissement Error - + Erreur Info - + Informations @@ -879,7 +880,7 @@ &Vertex Cache - + &Cache du Vertex @@ -889,37 +890,37 @@ Simple 2xAA - + 2x AA Off - + Off 2x - + 2x 4x - + 4x 8x - + 8x 16x - + 16x No translations - + Pas de traductions @@ -927,87 +928,87 @@ Cross - + Croix Circle - + Rond Square - + Carré Triangle - + Triangle Left Trigger - + Boutton L Right Trigger - + Boutton R Start - + Start Select - + Select Up - + Haut Down - + Bas Left - + Gauche Right - + Droite Home - + Accueil Stick left - + Stick : Gauche Stick right - + Stick : Droite Stick up - + Stick : Haut Stick bottom - + Stick : Bas diff --git a/Qt/languages/ppsspp_gr.ts b/Qt/languages/ppsspp_gr.ts new file mode 100644 index 000000000000..e90b6f6e91b3 --- /dev/null +++ b/Qt/languages/ppsspp_gr.ts @@ -0,0 +1,1013 @@ + + + + + Controls + + + Controls + Controls window title + Ôßôëïò ðáñáèýñïõ ÷åéñéóìþí + + + + CtrlDisAsmView + + + Copy &address + ÁíôéãñáöÞ &address + + + + Copy instruction (&hex) + ÁíôéãñáöÞ ïäçãßáò (&hex) + + + + Copy instruction (&disasm) + ÁíôéãñáöÞ ïäçãßáò (&disasm) + + + + &Run to here + &ÔñÝîéìï ùò åäþ + + + + &Set Next Statement + &Ïñéóìüò Åðüìåíç ÊáôÜóôáóçò + + + + &Toggle breakpoint + + + + + &Follow branch + &Áêïëïýèçóç êëÜäïõ + + + + Go to in &Memory View + ÌåôÜâáóç óå &ÐñïâïëÝá ÌíÞìçò + + + + &Rename function... + &Ìåôïíïìáóßá ëåéôïõñãßáò... + + + + New function name + ÍÝï üíïìá ëåéôïõñãßáò + + + + New function name: + ÍÝï üíïìá ëåéôïõñãßáò: + + + + CtrlMemView + + + Go to in &disasm + ÌåôÜâáóç óå &disasm + + + + &Copy value + &ÁíôéãñáöÞ ôéìÞò + + + + Dump... + Áðïôýðùóç... + + + + CtrlRegisterList + + + Go to in &memory view + ÌåôÜâáóç óå &ðñïâïëÞò ìíÞìçò + + + + Go to in &disasm + ÌåôÜâáóç óå &disasm + + + + &Copy value + &ÁíôéãñáöÞ ôéìÞò + + + + C&hange... + Á&ëëáãÞ... + + + + Set new value + Ïñéóìüò íÝáò ôéìÞò + + + + Set new value: + Ïñéóìüò íÝáò ôéìÞò: + + + + Debugger_Disasm + + + Disassembler + Window title + Ôßôëïò ðáñáèýñïõ + + + + Ctr: + + + + + &Go to + &ÌåôÜâáóç óå + + + + &PC + + + + + &LR + + + + + Show VFPU + ÅìöÜíéóç VFPU + + + + Regs + + + + + Funcs + + + + + &Go + &Îåêßíçìá + + + + Stop + ÓôáìÜôçìá + + + + Step &Into + ÌåôáðÞäçóç &óå + + + + Step &Over + ÌåôáðÞäçóç &ÐÜíù áðü + + + + S&kip + Á&ãíüçóç + + + + Next &HLE + Åðüìåíï &ÅÕÅ (HLE) + + + + Breakpoints + + + + + Address + Äéåýèõíóç + + + + Clear All + ÅêêáèÜñéóç ¼ëùí + + + + Callstack + + + + + Display Lists + Ëßóôåò Áðåéêüíéóçò + + + + + Id + + + + + + Status + ÊáôÜóôáóç + + + + Start Address + Áñ÷éêÞ Äéåýèõíóç + + + + Current Address + ÔñÝ÷ïõóá Äéåýèõíóç + + + + Run + Åêêßíçóç + + + + Step + ÂÞìá + + + + Threads + ÍÞìáôá + + + + Name + ¼íïìá + + + + Current PC + ÔñÝ÷ïí PC + + + + Entry point + Óçìåßï Ýíáñîçò + + + + Remove breakpoint + Áöáßñåóç óçìåßïõ äéáêïðÞò + + + + Go to entry point + ÌåôÜâáóç óôï óçìåßï åéóüäïõ + + + + Running + Óå ëåéôïõñãßá + + + + Wait + ÁíáìïíÞ + + + + Suspend + ÁíáóôïëÞ + + + + Show code + ÅìöÜíéóç êþäéêá + + + + Debugger_Memory + + + Dialog + ÄéÜëïãïò + + + + Goto: + ÌåôÜâáóç óå: + + + + Mode + Ëåéôïõñãßá + + + + Normal + Êáíïíéêü + + + + Symbols + Óýìâïëá + + + + Memory Viewer - %1 + ÐñïâïëÝáò ÌíÞìçò - %1 + + + + Debugger_VFPU + + + VFPU + + + + + Float + + + + + HalfFloat + + + + + Hex + Äåêáåîáäéêü + + + + Bytes + + + + + Shorts + + + + + Ints + + + + + GamePadDialog + + + Gamepad Configuration + Äéáìüñöùóç ×åéñéóôçñßïõ + + + + GamePad List + Ëßóôá ×åéñéóôçñßùí + + + + Refresh + ÁíáíÝùóç + + + + Select + ÅðéëïãÞ + + + + Gamepad Values : + ÔéìÝò ×åéñéóôçñßïõ : + + + + TextLabel + + + + + Assign Gamepad input + Åê÷þñçóç åéóüäïõ ÷åéñéóôçñßïõ + + + + to PSP button/axis + óôï êïõìß/Üîïíá PSP + + + + Assign + Åê÷þñçóç + + + + Press buttons on your gamePad to verify mapping : + ÐáôÞóôå ôá êïõìðéÜ óôï ÷åéñéóôÞñéï óáò ãéá íá åðéâåâáéþóåôå ôçí áíôéóôïß÷éóç: + + + + + <b>No gamepad</b> + <b>ÊáíÝíá ÷åéñéóôÞñéï</b> + + + + <b>Unknown gamepad</b> + <b>¢ãíùóôï ÷åéñéóôÞñéï</b> + + + + Buttons + ÐëÞêôñá + + + + + Button %1 + ÐëÞêôñï %1 + + + + Axes + ¢îïíåò + + + + %1 Neg + %1 Áñí. + + + + Axes %1 Neg + ¢îïíåò %1 Áñí. + + + + %1 Pos + %1 Èåô. + + + + Axes %1 Pos + ¢îïíåò %1 Èåô. + + + + Hats + Ìï÷ëïß + + + + <b>Current gamepad: %1</b> + <b>ÔñÝ÷ïí ÷åéñéóôÞñéï: %1</b> + + + + MainWindow + + + PPSSPP + + + + + &File + &Áñ÷åßï + + + + &Emulation + &Åîïìïßùóç + + + + Debu&g + ÁðïóöáëìÜôùó&ç + + + + &Options + &ÅðéëïãÝò + + + + G3D + + + + + HLE + ÅÕÅ(HLE) + + + + Default + ÐñïåðéëïãÞ + + + + Lo&g Levels + Åð&ßðåäá Êáôáãñáöéêïý + + + + &Language + &Ãëþóóá + + + + &Video + &Âßíôåï + + + + &Anisotropic filtering + &Áíéóïôñïðéêü öéôñÜñéóìá + + + + &Zoom + &ÌåãÝíèõóç + + + + Co&ntrols + ×å&éñéóôÞñéá + + + + &Core + &ÐõñÞíáò + + + + &Help + &ÂïÞèåéá + + + + &Open... + &¢íïéãìá... + + + + &Close + &Êëåßóéìï + + + + - + + + + + Quickload state + ÃñÞãïñç Öüñôùóç + + + + F4 + + + + + Quicksave state + ÃñÞãïñç ÁðïèÞêåõóç + + + + F2 + + + + + &Load State File... + &Öüñôùóç Óçìåßïõ ÁðïèÞêåõóçò... + + + + &Save State File... + &ÁðïèÞêåõóç Óçìåßïõ ÁðïèÞêåõóçò... + + + + E&xit + ¸&îïäïò + + + + &Run + &Åêêßíçóç + + + + F7 + + + + + &Pause + &Ðáýóç + + + + F8 + + + + + R&eset + Å&ðáíåêêßíçóç + + + + &Interpreter + + + + + &Slightly Faster Interpreter + &Åëáöñþò Ãñçãïñüôåñïò Interpreter + + + + &Dynarec + + + + + Load &Map File... + Öüñôùóç &Áñ÷åßïõ ×Üñôç... + + + + &Save Map File... + &ÁðïèÞêåõóç Áñ÷åßïõ ×Üñôç... + + + + &Reset Symbol Table + &ÅðáíáöïñÜ Ðßíáêá Óõìâüëùí + + + + &Disassembly + &Áðïóõíáñìïëüãçóç + + + + Ctrl+D + + + + + &Log Console + &Êïíóüëá ÊáôáãñáöÝá + + + + Ctrl+L + + + + + Memory &View... + ÅìöÜíéóç &ÌíÞìçò... + + + + Ctrl+M + + + + + &Keyboard + &Ðëçêôñïëüãéï + + + + &Toggle fullscreen + &ÌåôÜâáóç óå ðëÞñç ïèüíç + + + + Show &debug statistics + ÅìöÜíéóç &óôáôéóôéêþí áðïóöáëìÜôùóçò + + + + I&gnore illegal reads/writes + Á&ãíüçóç áèÝìéôùí áíáãíþóåùí + + + + &Gamepad + &×åéñéóôÞñéï + + + + Run on loa&d + Åêêßíçóç óôçí Ýíáñî&ç< + + + + Show &FPS counter + ÅìöÜíéóç ìåôñçôÞ &FPS + + + + S&tretch to display + Å&ðÝêôáóç óôçí ïèüíç + + + + &Sound emulation + &Åîïìïßùóç Þ÷ïõ + + + + F12 + + + + + &Buffered Rendering + &Buffered Áðåéêüíéóç + + + + F5 + + + + + &Hardware Transform + Hardware Ìåôáó÷çìáôéóìüò + + + + F6 + + + + + &Linear Filtering + Ãñáììéêü öéëôñÜñéóìá + + + + &Wireframe (experimental) + ÃñÜöçìá ÐëÝãìáôïò + + + + &Display Raw Framebuffer + &ÅìöÜíéóç ÁêáôÝñãáóôïõ Framebuffer + + + + Screen &1x + Ïèüíç &1x + + + + Ctrl+1 + + + + + Screen &2x + Ïèüíç &2x + + + + Ctrl+2 + + + + + Screen &3x + Ïèüíç &3x + + + + Ctrl+3 + + + + + Screen &4x + Ïèüíç &4x + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + &ÃñÞãïñç ÌíÞìç (dynarec, áóôáèÞò) + + + + &Go to http://www.ppsspp.org/ + &ÌåôÜâáóç óå http://www.ppsspp.org/ + + + + &About PPSSPP... + &Ðåñß ôïõ PPSSPP... + + + + &Use VBO + &×ñÞóç VBO + + + + + + Debug + ÁðïóöáëìÜôùóç + + + + + + Warning + Ðñïåéäïðïßçóç + + + + + + Error + ÓöÜëìá + + + + + + Info + Ðëçñïöïñßåò + + + + D&ump next frame to log + Å&îáãùãÞ åðüìåíïõ êáñÝ óôïí êáôáãñáöÝá + + + + &Vertex Cache + &ÐñïóïñçíÞ ÌíÞìç Êïñõöþí + + + + Memory View Texture... + ÐñïâïëÞ ÌíÞìçò Õöþí... + + + + Simple 2xAA + Áðëü 2xAA + + + + Off + + + + + 2x + + + + + 4x + + + + + 8x + + + + + 16x + + + + + No translations + Êáìßá ÌåôÜöñáóç + + + + gamepadMapping + + + Cross + ×ß + + + + Circle + Êýêëïò + + + + Square + ÔåôñÜãùíï + + + + Triangle + Ôñßãùíï + + + + Left Trigger + ÁñéóôåñÞ óêáíäÜëç + + + + Right Trigger + ÄåîéÜ óêáíäÜëç + + + + Start + + + + + Select + + + + + Up + ÐÜíù + + + + Down + ÊÜôù + + + + Left + ÁñéóôåñÜ + + + + Right + ÄåîéÜ + + + + Home + + + + + Stick left + Áíáëïãéêü áñéóôåñÜ + + + + Stick right + Áíáëïãéêü äåîéÜ + + + + Stick up + Áíáëïãéêü ðÜíù + + + + Stick bottom + Áíáëïãéêü êÜôù + + + diff --git a/Qt/languages/ppsspp_id.ts b/Qt/languages/ppsspp_id.ts new file mode 100644 index 000000000000..41806c00f274 --- /dev/null +++ b/Qt/languages/ppsspp_id.ts @@ -0,0 +1,1013 @@ + + + + + Controls + + + Controls + Controls window title + + + + + CtrlDisAsmView + + + Copy &address + + + + + Copy instruction (&hex) + + + + + Copy instruction (&disasm) + + + + + &Run to here + + + + + &Set Next Statement + + + + + &Toggle breakpoint + + + + + &Follow branch + + + + + Go to in &Memory View + + + + + &Rename function... + + + + + New function name + + + + + New function name: + + + + + CtrlMemView + + + Go to in &disasm + + + + + &Copy value + + + + + Dump... + + + + + CtrlRegisterList + + + Go to in &memory view + + + + + Go to in &disasm + + + + + &Copy value + + + + + C&hange... + + + + + Set new value + + + + + Set new value: + + + + + Debugger_Disasm + + + Disassembler + Window title + + + + + Ctr: + + + + + &Go to + + + + + &PC + + + + + &LR + + + + + Show VFPU + + + + + Regs + + + + + Funcs + + + + + &Go + + + + + Stop + + + + + Step &Into + + + + + Step &Over + + + + + S&kip + + + + + Next &HLE + + + + + Breakpoints + + + + + Address + + + + + Clear All + + + + + Callstack + + + + + Display Lists + + + + + + Id + + + + + + Status + + + + + Start Address + + + + + Current Address + + + + + Run + + + + + Step + + + + + Threads + + + + + Name + + + + + Current PC + + + + + Entry point + + + + + Remove breakpoint + + + + + Go to entry point + + + + + Running + + + + + Wait + + + + + Suspend + + + + + Show code + + + + + Debugger_Memory + + + Dialog + + + + + Goto: + + + + + Mode + + + + + Normal + + + + + Symbols + + + + + Memory Viewer - %1 + + + + + Debugger_VFPU + + + VFPU + + + + + Float + + + + + HalfFloat + + + + + Hex + + + + + Bytes + + + + + Shorts + + + + + Ints + + + + + GamePadDialog + + + Gamepad Configuration + + + + + GamePad List + + + + + Refresh + + + + + Select + + + + + Gamepad Values : + + + + + TextLabel + + + + + Assign Gamepad input + + + + + to PSP button/axis + + + + + Assign + + + + + Press buttons on your gamePad to verify mapping : + + + + + + <b>No gamepad</b> + + + + + <b>Unknown gamepad</b> + + + + + Buttons + Tombol + + + + + Button %1 + + + + + Axes + + + + + %1 Neg + + + + + Axes %1 Neg + + + + + %1 Pos + + + + + Axes %1 Pos + + + + + Hats + + + + + <b>Current gamepad: %1</b> + + + + + MainWindow + + + PPSSPP + + + + + &File + + + + + &Emulation + + + + + Debu&g + + + + + &Options + + + + + G3D + + + + + HLE + + + + + Default + + + + + Lo&g Levels + + + + + &Language + + + + + &Video + + + + + &Anisotropic filtering + + + + + &Zoom + + + + + Co&ntrols + + + + + &Core + + + + + &Help + + + + + &Open... + + + + + &Close + + + + + - + + + + + Quickload state + + + + + F4 + + + + + Quicksave state + + + + + F2 + + + + + &Load State File... + + + + + &Save State File... + + + + + E&xit + + + + + &Run + + + + + F7 + + + + + &Pause + + + + + F8 + + + + + R&eset + + + + + &Interpreter + + + + + &Slightly Faster Interpreter + + + + + &Dynarec + + + + + Load &Map File... + + + + + &Save Map File... + + + + + &Reset Symbol Table + + + + + &Disassembly + + + + + Ctrl+D + + + + + &Log Console + + + + + Ctrl+L + + + + + Memory &View... + + + + + Ctrl+M + + + + + &Keyboard + + + + + &Toggle fullscreen + + + + + Show &debug statistics + + + + + I&gnore illegal reads/writes + + + + + &Gamepad + + + + + Run on loa&d + + + + + Show &FPS counter + + + + + S&tretch to display + + + + + &Sound emulation + + + + + F12 + + + + + &Buffered Rendering + + + + + F5 + + + + + &Hardware Transform + + + + + F6 + + + + + &Linear Filtering + + + + + &Wireframe (experimental) + + + + + &Display Raw Framebuffer + + + + + Screen &1x + + + + + Ctrl+1 + + + + + Screen &2x + + + + + Ctrl+2 + + + + + Screen &3x + + + + + Ctrl+3 + + + + + Screen &4x + + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + + + + + &Go to http://www.ppsspp.org/ + + + + + &About PPSSPP... + + + + + &Use VBO + + + + + + + Debug + + + + + + + Warning + Peringatan + + + + + + Error + Galat + + + + + + Info + Informasi + + + + D&ump next frame to log + + + + + &Vertex Cache + + + + + Memory View Texture... + + + + + Simple 2xAA + + + + + Off + Mati + + + + 2x + + + + + 4x + + + + + 8x + + + + + 16x + + + + + No translations + Tak ada terjemahan + + + + gamepadMapping + + + Cross + Silang + + + + Circle + Bundar + + + + Square + Kotak + + + + Triangle + Segitiga + + + + Left Trigger + Trigger Kiri + + + + Right Trigger + Trigger Kanan + + + + Start + Start + + + + Select + Select + + + + Up + Atas + + + + Down + Bawah + + + + Left + Kiri + + + + Right + Kanan + + + + Home + Beranda + + + + Stick left + Stick kiri + + + + Stick right + Stick kanan + + + + Stick up + Stick atas + + + + Stick bottom +        Stick bawah + + + diff --git a/Qt/languages/ppsspp_pt-br.ts b/Qt/languages/ppsspp_pt-br.ts new file mode 100644 index 000000000000..6176bbfb0cb7 --- /dev/null +++ b/Qt/languages/ppsspp_pt-br.ts @@ -0,0 +1,1013 @@ + + + + + Controls + + + Controls + Controls window title + Controles + + + + CtrlDisAsmView + + + Copy &address + Copiar &endereço + + + + Copy instruction (&hex) + Copiar instrução (&hex) + + + + Copy instruction (&disasm) + Copiar instrução (&disam) + + + + &Run to here + &Rodar aqui + + + + &Set Next Statement + + + + + &Toggle breakpoint + &Ativar breakpoint + + + + &Follow branch + &Seguir caminho + + + + Go to in &Memory View + Vá para o Visualizador de &Memória + + + + &Rename function... + &Renomear função... + + + + New function name + Novo nome de função + + + + New function name: + Novo nome de função: + + + + CtrlMemView + + + Go to in &disasm + Vá para &disasm + + + + &Copy value + &Copiar valor + + + + Dump... + Despejar... + + + + CtrlRegisterList + + + Go to in &memory view + Vá para o visualizador de &memória + + + + Go to in &disasm + Vá para o &disasm + + + + &Copy value + &Copiar valor + + + + C&hange... + &Trocar... + + + + Set new value + Setar novo valor + + + + Set new value: + Setar novo valor: + + + + Debugger_Disasm + + + Disassembler + Window title + + + + + Ctr: + + + + + &Go to + &Vá para + + + + &PC + + + + + &LR + + + + + Show VFPU + Mostrar VFPU + + + + Regs + + + + + Funcs + + + + + &Go + &Ir + + + + Stop + Parar + + + + Step &Into + + + + + Step &Over + + + + + S&kip + P&ular + + + + Next &HLE + Próximo &HLE + + + + Breakpoints + + + + + Address + Endereços + + + + Clear All + Limpar Tudo + + + + Callstack + + + + + Display Lists + Mostrar Listas + + + + + Id + + + + + + Status + Estado + + + + Start Address + Endereço inicial + + + + Current Address + Endereço atual + + + + Run + Rodar + + + + Step + Passo + + + + Threads + + + + + Name + Nome + + + + Current PC + PC corrente + + + + Entry point + Ponto de entrada + + + + Remove breakpoint + Remover breakpoint + + + + Go to entry point + Ir para ponto de entrada + + + + Running + Rodando + + + + Wait + Esperando + + + + Suspend + Suspenso + + + + Show code + Mostrar código + + + + Debugger_Memory + + + Dialog + Diálogo + + + + Goto: + Vá para: + + + + Mode + Modo + + + + Normal + + + + + Symbols + Símbolos + + + + Memory Viewer - %1 + Visualizador de Memória - %1 + + + + Debugger_VFPU + + + VFPU + + + + + Float + + + + + HalfFloat + + + + + Hex + + + + + Bytes + + + + + Shorts + + + + + Ints + + + + + GamePadDialog + + + Gamepad Configuration + Configuração do Gamepad + + + + GamePad List + Lista de Gamepads + + + + Refresh + Atualizar + + + + Select + Selecionar + + + + Gamepad Values : + Valores de Gamepad : + + + + TextLabel + + + + + Assign Gamepad input + Atribuir entrada do Gamepad + + + + to PSP button/axis + para o botão/eixo do PSP + + + + Assign + Atribuir + + + + Press buttons on your gamePad to verify mapping : + Pressione botões no seu Gamepad para verificar o mapeamento : + + + + + <b>No gamepad</b> + Sem Gamepad + + + + <b>Unknown gamepad</b> + Gamepad desconhecido + + + + Buttons + Botões + + + + + Button %1 + Botões %1 + + + + Axes + Eixos + + + + %1 Neg + + + + + Axes %1 Neg + Eixos %1 Neg + + + + %1 Pos + + + + + Axes %1 Pos + Eixos %1 Pos + + + + Hats + + + + + <b>Current gamepad: %1</b> + <b>Gamepad atual: %1</b> + + + + MainWindow + + + PPSSPP + + + + + &File + &Arquivo + + + + &Emulation + &Emulação + + + + Debu&g + + + + + &Options + &Opções + + + + G3D + + + + + HLE + + + + + Default + Padrão + + + + Lo&g Levels + Nível de Lo&g + + + + &Language + &Linguagem + + + + &Video + &Vídeo + + + + &Anisotropic filtering + Filtragem &Anisotrópica + + + + &Zoom + + + + + Co&ntrols + Co&ntroles + + + + &Core + &Núcleo + + + + &Help + Aj&uda + + + + &Open... + &Abrir... + + + + &Close + &Fechar + + + + - + + + + + Quickload state + Carregar estado rápido + + + + F4 + + + + + Quicksave state + Salvar estado rápido + + + + F2 + + + + + &Load State File... + &Carregar estado do arquivo... + + + + &Save State File... + &Salvar estado do arquivo... + + + + E&xit + Sai&r + + + + &Run + &Rodar + + + + F7 + + + + + &Pause + &Pausar + + + + F8 + + + + + R&eset + R&esetar + + + + &Interpreter + &Interpretador + + + + &Slightly Faster Interpreter + Interpretador um &pouco mais rápido + + + + &Dynarec + Recompilador &dinâmico + + + + Load &Map File... + Carregar &mapa no arquivo... + + + + &Save Map File... + &Salvar mapa no arquivo... + + + + &Reset Symbol Table + &Reiniciar tabela de símbolos + + + + &Disassembly + + + + + Ctrl+D + L + + + + &Log Console + Console de &Logs + + + + Ctrl+L + L + + + + Memory &View... + &Visualizador de Memória... + + + + Ctrl+M + + + + + &Keyboard + &Teclado + + + + &Toggle fullscreen + Tela &cheia + + + + Show &debug statistics + Mostrar estatísticas de &debug + + + + I&gnore illegal reads/writes + I&gnorar leituras/escritas ilegais + + + + &Gamepad + + + + + Run on loa&d + Rodar ao &carregar + + + + Show &FPS counter + Mostrar contador de &FPS + + + + S&tretch to display + Es&ticar tela + + + + &Sound emulation + Emular &som + + + + F12 + + + + + &Buffered Rendering + Renderização com &buffer + + + + F5 + + + + + &Hardware Transform + Transformação em &Hardware + + + + F6 + + + + + &Linear Filtering + Filtragem &Linear + + + + &Wireframe (experimental) + &Wireframe (experimental) + + + + &Display Raw Framebuffer + Mostrar framebuffer &bruto + + + + Screen &1x + &1x Tela' + + + + Ctrl+1 + + + + + Screen &2x + &2x Tela + + + + Ctrl+2 + + + + + Screen &3x + &3x Tela + + + + Ctrl+3 + + + + + Screen &4x + &4x Tela + + + + Ctrl+4 + + + + + &Fast Memory (dynarec, unstable) + Memória &rápida (recompilador dinâmico, instável) + + + + &Go to http://www.ppsspp.org/ + &Ir para http://www.ppsspp.org/ + + + + &About PPSSPP... + &Sobre o PPSSPP... + + + + &Use VBO + &Usar VBO + + + + + + Debug + + + + + + + Warning + Atenção + + + + + + Error + Erro + + + + + + Info + Informação + + + + D&ump next frame to log + D&espejar o próximo frame no log + + + + &Vertex Cache + Cache do &Vertex + + + + Memory View Texture... + Ver texturas na memória... + + + + Simple 2xAA + Simples 2xAA + + + + Off + Desligar + + + + 2x + + + + + 4x + + + + + 8x + + + + + 16x + + + + + No translations + Sem tradução + + + + gamepadMapping + + + Cross + X + + + + Circle + Círculo + + + + Square + Quadrado + + + + Triangle + Triângulo + + + + Left Trigger + Gatilho Esquerdo + + + + Right Trigger + Gatilho Direito + + + + Start + + + + + Select + + + + + Up + Cima + + + + Down + Baixo + + + + Left + Esquerda + + + + Right + Direita + + + + Home + + + + + Stick left + Analógico Esquerda + + + + Stick right + Analógico Direita + + + + Stick up + Analógico Cima + + + + Stick bottom + Analógico Baixo + + + diff --git a/Qt/languages/ppsspp_tc.ts b/Qt/languages/ppsspp_tc.ts index a42937ece9f6..8b4520c1a079 100644 --- a/Qt/languages/ppsspp_tc.ts +++ b/Qt/languages/ppsspp_tc.ts @@ -1,1014 +1,1014 @@ - - - - - Controls - - - Controls - Controls window title - 控制器 - - - - CtrlDisAsmView - - - Copy &address - (&A)è¤‡è£½åœ°å€ - - - - Copy instruction (&hex) - (&H)複製指令(å六進制型) - - - - Copy instruction (&disasm) - (&D)複製指令(å彙編型) - - - - &Run to here - (&R)執行到此處 - - - - &Set Next Statement - (&S)è¨­ç½®ä¸‹ä¸€èªžå¥ - - - - &Toggle breakpoint - (&T)鎖定斷點 - - - - &Follow branch - (&F)跟隨分支 - - - - Go to in &Memory View - (&M)轉到內存視圖 - - - - &Rename function... - (&R)é‡å‘½å函數... - - - - New function name - 新函數å稱 - - - - New function name: - - - - - CtrlMemView - - - Go to in &disasm - (&D)轉到å彙編器 - - - - &Copy value - (&C)複製值 - - - - Dump... - 轉儲... - - - - CtrlRegisterList - - - Go to in &memory view - (&M)轉到內存視圖 - - - - Go to in &disasm - (&D)轉到å彙編器 - - - - &Copy value - (&C)複製值 - - - - C&hange... - (&H)更改... - - - - Set new value - 設置新值 - - - - Set new value: - - - - - Debugger_Disasm - - - Disassembler - Window title - - - - - Ctr: - 計數器: - - - - &Go to - (&G)轉到 - - - - &PC - (&P)PC寄存器 - - - - &LR - (&L)連接寄存器 - - - - Show VFPU - 顯示VFPU - - - - Regs - 寄存器 - - - - Funcs - 函數 - - - - &Go - (&G)轉到 - - - - Stop - åœæ­¢ - - - - Step &Into - (&I)單步跟進 - - - - Step &Over - (&O)å–®æ­¥æ­¥éŽ - - - - S&kip - (&K)è·³éŽ - - - - Next &HLE - (&H)下一HLE - - - - Breakpoints - 斷點 - - - - Address - åœ°å€ - - - - Clear All - 清空所有 - - - - Callstack - 調用棧 - - - - Display Lists - 顯示列表 - - - - - Id - Id - - - - - Status - 狀態 - - - - Start Address - èµ·å§‹åœ°å€ - - - - Current Address - 當å‰åœ°å€ - - - - Run - 執行 - - - - Step - 單步執行 - - - - Threads - 線程 - - - - Name - å稱 - - - - Current PC - 當å‰PC寄存器 - - - - Entry point - å…¥å£é»ž - - - - Remove breakpoint - 移除斷點 - - - - Go to entry point - 轉到入å£é»ž - - - - Running - 執行中 - - - - Wait - 等待 - - - - Suspend - 掛起 - - - - Show code - 顯示代碼 - - - - Debugger_Memory - - - Dialog - å°è©±æ¡† - - - - Goto: - 轉到: - - - - Mode - æ¨¡å¼ - - - - Normal - æ™®é€šæ¨¡å¼ - - - - Symbols - ç‰¹å¾µç¬¦æ¨¡å¼ - - - - Memory Viewer - %1 - - - - - Debugger_VFPU - - - VFPU - - - - - Float - 浮點型 - - - - HalfFloat - åŠæµ®é»žåž‹ - - - - Hex - å六進制型 - - - - Bytes - 字節型 - - - - Shorts - 短整型 - - - - Ints - æ•´åž‹ - - - - GamePadDialog - - - Gamepad Configuration - 手柄設置 - - - - GamePad List - 手柄列表 - - - - Refresh - 刷新 - - - - Select - é¸ä¸­ - - - - Gamepad Values : - 手柄值: - - - - TextLabel - TextLabel - TextLabel - - - - Assign Gamepad input - 指定該手柄 - - - - to PSP button/axis - 作為PSP按éµ/æ–桿輸入 - - - - Assign - 指定 - - - - Press buttons on your gamePad to verify mapping : - 按下手柄按éµä¾†ç¢ºèªéµä½æ˜ å°„: - - - - - <b>No gamepad</b> - <b>未發ç¾æ‰‹æŸ„</b> - - - - <b>Unknown gamepad</b> - <b>未識別的手柄</b> - - - - Buttons - 按鈕 - - - - - Button %1 - 按鈕 %1 - - - - Axes - æ–æ¡¿ - - - - %1 Neg - %1 Neg - - - - Axes %1 Neg - Axes %1 Neg - - - - %1 Pos - %1 Pos - - - - Axes %1 Pos - Axes %1 Pos - - - - Hats - Hats - - - - <b>Current gamepad: %1</b> - <b>當å‰æ‰‹æŸ„: %1</b> - - - - MainWindow - - - PPSSPP - PPSSPP - - - - &File - (&F)文件 - - - - &Emulation - (&E)模擬 - - - - Debu&g - (&G)除錯 - - - - &Options - (&O)é¸é … - - - - G3D - - - - - HLE - - - - - Default - é»˜èª - - - - Lo&g Levels - - - - - &Language - - - - - &Video - - - - - &Anisotropic filtering - - - - - &Zoom - - - - - Co&ntrols - - - - - &Core - - - - - &Help - (&H)幫助 - - - - &Open... - (&O)打開... - - - - &Close - (&C)關閉 - - - - - - - - - - - Quickload state - 快速讀檔 - - - - F4 - F4 - - - - Quicksave state - 快速存檔 - - - - F2 - F2 - - - - &Load State File... - (&L)讀å–存檔... - - - - &Save State File... - (&S)ä¿å­˜å­˜æª”... - - - - E&xit - (&X)退出 - - - - &Run - (&R)é‹è¡Œ - - - - F7 - F7 - - - - &Pause - (&P)æš«åœ - - - - F8 - F8 - - - - R&eset - (&E)é‡ç½® - - - - &Interpreter - (&I)解釋器 - - - - &Slightly Faster Interpreter - (&S)較快的解釋器 - - - - &Dynarec - (&D)動態解釋器 - - - - Load &Map File... - (&M)讀å–Map文件... - - - - &Save Map File... - (&S)ä¿å­˜Map文件... - - - - &Reset Symbol Table - (&R)é‡ç½®ç¬¦è™Ÿè¡¨ - - - - &Disassembly - (&D)å彙編器 - - - - Ctrl+D - Ctrl+D - - - - &Log Console - (&L)查看控制å°æ—¥èªŒ - - - - Ctrl+L - Ctrl+L - - - - Memory &View... - (&V)內存視圖... - - - - Ctrl+M - Ctrl+M - - - - &Keyboard - - - - - &Toggle fullscreen - - - - - Show &debug statistics - - - - - I&gnore illegal reads/writes - - - - - &Gamepad - - - - - Run on loa&d - - - - - Show &FPS counter - - - - - S&tretch to display - - - - - &Sound emulation - - - - - F12 - F12 - - - - &Buffered Rendering - (&B)æ¸²æŸ“ç·©è¡ - - - - F5 - F5 - - - - &Hardware Transform - (&H)硬件加速 - - - - F6 - F6 - - - - &Linear Filtering - (&L)線性éŽæ¿¾ - - - - &Wireframe (experimental) - (&W)使用線框 (實驗性) - - - - &Display Raw Framebuffer - (&D)顯示原始?ç·©è¡ - - - - Screen &1x - &1å€çª—å£ - - - - Ctrl+1 - Ctrl+1 - - - - Screen &2x - &2å€çª—å£ - - - - Ctrl+2 - Ctrl+2 - - - - Screen &3x - &3å€çª—å£ - - - - Ctrl+3 - Ctrl+3 - - - - Screen &4x - &4å€çª—å£ - - - - Ctrl+4 - Ctrl+4 - - - - &Fast Memory (dynarec, unstable) - (&F)高速閃存 (å‹•æ…‹é‡ç·¨è­¯,ä¸ç©©å®š) - - - - &Go to http://www.ppsspp.org/ - (&G)轉到 http://www.ppsspp.org/ - - - - &About PPSSPP... - (&A)關於 PPSSPP... - - - - &Use VBO - (&U)使用 VBO - - - - - - Debug - 除錯 - - - - - - Warning - 警告 - - - - - - Error - 錯誤 - - - - - - Info - ä¿¡æ¯ - - - - D&ump next frame to log - (&U)將下一幀轉儲至日誌 - - - - &Vertex Cache - (&V)頂點緩存 - - - - Memory View Texture... - 內存紋ç†è¦–圖... - - - - Simple 2xAA - - - - - Off - - - - - 2x - - - - - 4x - - - - - 8x - - - - - 16x - - - - - No translations - 未翻譯 - - - - gamepadMapping - - - Cross - × - - - - Circle - â—‹ - - - - Square - â–¡ - - - - Triangle - â–³ - - - - Left Trigger - L - - - - Right Trigger - R - - - - Start - 開始 - - - - Select - é¸æ“‡ - - - - Up - ↑ - - - - Down - ↓ - - - - Left - ↠- - - - Right - → - - - - Home - Home - - - - Stick left - æ–æ¡¿å·¦ - - - - Stick right - æ–æ¡¿å³ - - - - Stick up - æ–桿上 - - - - Stick bottom - æ–桿下 - - - + + + + + Controls + + + Controls + Controls window title + 控制器 + + + + CtrlDisAsmView + + + Copy &address + (&A)è¤‡è£½åœ°å€ + + + + Copy instruction (&hex) + (&H)複製指令(å六進制型) + + + + Copy instruction (&disasm) + (&D)複製指令(彙編型) + + + + &Run to here + (&R)執行到此處 + + + + &Set Next Statement + (&S)è¨­ç½®ä¸‹ä¸€èªžå¥ + + + + &Toggle breakpoint + (&T)鎖定斷點 + + + + &Follow branch + (&F)跟隨分支 + + + + Go to in &Memory View + (&M)轉到內存視圖 + + + + &Rename function... + (&R)é‡å‘½å函數... + + + + New function name + 新函數å稱 + + + + New function name: + 新函數å稱: + + + + CtrlMemView + + + Go to in &disasm + (&D)轉到彙編視圖 + + + + &Copy value + (&C)複製值 + + + + Dump... + 轉儲... + + + + CtrlRegisterList + + + Go to in &memory view + (&M)轉到內存視圖 + + + + Go to in &disasm + (&D)轉到彙編視圖 + + + + &Copy value + (&C)複製值 + + + + C&hange... + (&H)更改... + + + + Set new value + 設置新值 + + + + Set new value: + 設置新值: + + + + Debugger_Disasm + + + Disassembler + Window title + å彙編器 + + + + Ctr: + 計數器: + + + + &Go to + (&G)轉到 + + + + &PC + (&P)PC寄存器 + + + + &LR + (&L)連接寄存器 + + + + Show VFPU + 顯示VFPU + + + + Regs + 寄存器 + + + + Funcs + 函數 + + + + &Go + (&G)轉到 + + + + Stop + åœæ­¢ + + + + Step &Into + (&I)單步跟進 + + + + Step &Over + (&O)å–®æ­¥æ­¥éŽ + + + + S&kip + (&K)è·³éŽ + + + + Next &HLE + (&H)下一HLE + + + + Breakpoints + 斷點 + + + + Address + åœ°å€ + + + + Clear All + 清空所有 + + + + Callstack + 調用棧 + + + + Display Lists + 顯示列表 + + + + + Id + Id + + + + + Status + 狀態 + + + + Start Address + èµ·å§‹åœ°å€ + + + + Current Address + 當å‰åœ°å€ + + + + Run + 執行 + + + + Step + 單步執行 + + + + Threads + 線程 + + + + Name + å稱 + + + + Current PC + 當å‰PC寄存器 + + + + Entry point + å…¥å£é»ž + + + + Remove breakpoint + 移除斷點 + + + + Go to entry point + 轉到入å£é»ž + + + + Running + 執行中 + + + + Wait + 等待 + + + + Suspend + 掛起 + + + + Show code + 顯示代碼 + + + + Debugger_Memory + + + Dialog + å°è©±æ¡† + + + + Goto: + 轉到: + + + + Mode + æ¨¡å¼ + + + + Normal + æ™®é€šæ¨¡å¼ + + + + Symbols + ç‰¹å¾µç¬¦æ¨¡å¼ + + + + Memory Viewer - %1 + 內存查看器 - %1 + + + + Debugger_VFPU + + + VFPU + VFPU + + + + Float + 浮點型 + + + + HalfFloat + åŠæµ®é»žåž‹ + + + + Hex + å六進制型 + + + + Bytes + 字節型 + + + + Shorts + 短整型 + + + + Ints + æ•´åž‹ + + + + GamePadDialog + + + Gamepad Configuration + 手柄設置 + + + + GamePad List + 手柄列表 + + + + Refresh + 刷新 + + + + Select + é¸ä¸­ + + + + Gamepad Values : + 手柄值: + + + + TextLabel + TextLabel + TextLabel + + + + Assign Gamepad input + 指定該手柄 + + + + to PSP button/axis + 作為PSP按éµ/æ–桿輸入 + + + + Assign + 指定 + + + + Press buttons on your gamePad to verify mapping : + 按下手柄按éµä¾†ç¢ºèªéµä½æ˜ å°„: + + + + + <b>No gamepad</b> + <b>未發ç¾æ‰‹æŸ„</b> + + + + <b>Unknown gamepad</b> + <b>未識別的手柄</b> + + + + Buttons + 按鈕 + + + + + Button %1 + 按鈕 %1 + + + + Axes + æ–æ¡¿ + + + + %1 Neg + %1 Neg + + + + Axes %1 Neg + Axes %1 Neg + + + + %1 Pos + %1 Pos + + + + Axes %1 Pos + Axes %1 Pos + + + + Hats + Hats + + + + <b>Current gamepad: %1</b> + <b>當å‰æ‰‹æŸ„: %1</b> + + + + MainWindow + + + PPSSPP + PPSSPP + + + + &File + (&F)文件 + + + + &Emulation + (&E)模擬 + + + + Debu&g + (&G)調試 + + + + &Options + (&O)é¸é … + + + + G3D + + + + + HLE + + + + + Default + é»˜èª + + + + Lo&g Levels + (&G)日誌級別 + + + + &Language + (&L)語言é¸æ“‡ + + + + &Video + (&V)視頻 + + + + &Anisotropic filtering + (&A)å„å‘異性éŽæ¿¾ + + + + &Zoom + (&Z)縮放 + + + + Co&ntrols + (&N)控制 + + + + &Core + (&C)核心 + + + + &Help + (&H)幫助 + + + + &Open... + (&O)打開... + + + + &Close + (&C)關閉 + + + + - + - + + + + Quickload state + 快速讀檔 + + + + F4 + F4 + + + + Quicksave state + 快速存檔 + + + + F2 + F2 + + + + &Load State File... + (&L)讀å–存檔... + + + + &Save State File... + (&S)ä¿å­˜å­˜æª”... + + + + E&xit + (&X)退出 + + + + &Run + (&R)é‹è¡Œ + + + + F7 + F7 + + + + &Pause + (&P)æš«åœ + + + + F8 + F8 + + + + R&eset + (&E)é‡ç½® + + + + &Interpreter + (&I)解釋器 + + + + &Slightly Faster Interpreter + (&S)較快的解釋器 + + + + &Dynarec + (&D)å‹•æ…‹é‡ç·¨è­¯ + + + + Load &Map File... + (&M)讀å–Map文件... + + + + &Save Map File... + (&S)ä¿å­˜Map文件... + + + + &Reset Symbol Table + (&R)é‡ç½®ç¬¦è™Ÿè¡¨ + + + + &Disassembly + (&D)彙編視圖 + + + + Ctrl+D + Ctrl+D + + + + &Log Console + (&L)查看控制å°æ—¥èªŒ + + + + Ctrl+L + Ctrl+L + + + + Memory &View... + (&V)內存視圖... + + + + Ctrl+M + Ctrl+M + + + + &Keyboard + (&K)éµç›¤ + + + + &Toggle fullscreen + (&T)å…¨å± + + + + Show &debug statistics + (&D)é¡¯ç¤ºèª¿è©¦ä¿¡æ¯ + + + + I&gnore illegal reads/writes + (&G)忽略éžæ³•è®€å¯« + + + + &Gamepad + (&G)手柄 + + + + Run on loa&d + (&D)載入時é‹è¡Œ + + + + Show &FPS counter + (&F)顯示幀率計數器 + + + + S&tretch to display + (&T)拉伸顯示 + + + + &Sound emulation + (&S)模擬è²éŸ³ + + + + F12 + F12 + + + + &Buffered Rendering + (&B)æ¸²æŸ“ç·©è¡ + + + + F5 + F5 + + + + &Hardware Transform + (&H)硬件加速 + + + + F6 + F6 + + + + &Linear Filtering + (&L)線性éŽæ¿¾ + + + + &Wireframe (experimental) + (&W)使用線框 (實驗性é¸é …) + + + + &Display Raw Framebuffer + (&D)顯示 Raw åŽŸå§‹ç·©è¡ + + + + Screen &1x + &1å€çª—å£ + + + + Ctrl+1 + Ctrl+1 + + + + Screen &2x + &2å€çª—å£ + + + + Ctrl+2 + Ctrl+2 + + + + Screen &3x + &3å€çª—å£ + + + + Ctrl+3 + Ctrl+3 + + + + Screen &4x + &4å€çª—å£ + + + + Ctrl+4 + Ctrl+4 + + + + &Fast Memory (dynarec, unstable) + (&F)高速閃存 (å‹•æ…‹é‡ç·¨è­¯,ä¸ç©©å®š) + + + + &Go to http://www.ppsspp.org/ + (&G)轉到 http://www.ppsspp.org/ + + + + &About PPSSPP... + (&A)關於 PPSSPP... + + + + &Use VBO + (&U)使用 VBO + + + + + + Debug + 調試 + + + + + + Warning + 警告 + + + + + + Error + 錯誤 + + + + + + Info + ä¿¡æ¯ + + + + D&ump next frame to log + (&U)將下一幀轉儲至日誌 + + + + &Vertex Cache + (&V)頂點緩存 + + + + Memory View Texture... + 內存紋ç†è¦–圖... + + + + Simple 2xAA + + + + + Off + 關閉 + + + + 2x + 2x + + + + 4x + 4x + + + + 8x + 8x + + + + 16x + 16x + + + + No translations + 未翻譯 + + + + gamepadMapping + + + Cross + × + + + + Circle + â—‹ + + + + Square + â–¡ + + + + Triangle + â–³ + + + + Left Trigger + L + + + + Right Trigger + R + + + + Start + 開始 + + + + Select + é¸æ“‡ + + + + Up + ↑ + + + + Down + ↓ + + + + Left + ↠+ + + + Right + → + + + + Home + Home + + + + Stick left + æ–æ¡¿å·¦ + + + + Stick right + æ–æ¡¿å³ + + + + Stick up + æ–桿上 + + + + Stick bottom + æ–桿下 + + + diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index c6b45a5dcc4a..eaec0249aa9c 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -16,13 +16,14 @@ #include "ConsoleListener.h" #include "base/display.h" #include "GPU/GPUInterface.h" -#include "GPU/GPUState.h" #include "QtHost.h" #include "EmuThread.h" const char *stateToLoad = NULL; +// TODO: Make this class thread-aware. Can't send events to a different thread. Currently only works on X11. +// Needs to use QueuedConnection for signals/slots. MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -33,7 +34,6 @@ MainWindow::MainWindow(QWidget *parent) : displaylistWindow(0) { ui->setupUi(this); - qApp->installEventFilter(this); controls = new Controls(this); #if QT_HAS_SDL @@ -57,25 +57,23 @@ MainWindow::MainWindow(QWidget *parent) : SetZoom(zoom); EmuThread_Start(emugl); + SetGameTitle(fileToStart); if (!fileToStart.isNull()) { - SetGameTitle(fileToStart); - UpdateMenus(); - EmuThread_StartGame(fileToStart); + UpdateMenus(); if (stateToLoad != NULL) SaveState::Load(stateToLoad); } - else - SetGameTitle(""); } MainWindow::~MainWindow() { delete ui; } + void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID) { std::string config_filename; @@ -154,10 +152,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co if (g_Config.currentDirectory == "") { - g_Config.currentDirectory = getenv("HOME"); + g_Config.currentDirectory = QDir::homePath().toStdString(); } - g_Config.memCardDirectory = std::string(getenv("HOME"))+"/.ppsspp/"; + g_Config.memCardDirectory = QDir::homePath().toStdString()+"/.ppsspp/"; g_Config.flashDirectory = g_Config.memCardDirectory+"/flash/"; LogManager::Init(); @@ -173,110 +171,91 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co #endif } -void MainWindow::SetNextState(CoreState state) +void MainWindow::ShowMemory(u32 addr) { - nextState = state; + if(memoryWindow) + memoryWindow->Goto(addr); } -void MainWindow::SetGameTitle(QString text) +void MainWindow::Update() { - QString title = "PPSSPP " + QString(PPSSPP_VERSION_STR); - if (text != "") - title += QString(" - %1").arg(text); - - setWindowTitle(title); -} + UpdateInputState(&input_state); -void MainWindow::BrowseAndBoot(void) -{ - QString filename = QFileDialog::getOpenFileName(NULL, "Load File", g_Config.currentDirectory.c_str(), "PSP ROMs (*.pbp *.elf *.iso *.cso *.prx)"); - if (QFile::exists(filename)) + for (int i = 0; i < controllistCount; i++) { - QFileInfo info(filename); - g_Config.currentDirectory = info.absolutePath().toStdString(); - EmuThread_StartGame(filename); + if (pressedKeys.contains(controllist[i].key) || + input_state.pad_buttons_down & controllist[i].emu_id) + __CtrlButtonDown(controllist[i].psp_id); + else + __CtrlButtonUp(controllist[i].psp_id); } -} - -void MainWindow::Boot() -{ - dialogDisasm = new Debugger_Disasm(currentDebugMIPS, this, this); - if(g_Config.bShowDebuggerOnLoad) - dialogDisasm->show(); - - if(g_Config.bFullScreen != isFullScreen()) - on_action_OptionsFullScreen_triggered(); - - memoryWindow = new Debugger_Memory(currentDebugMIPS, this, this); - memoryTexWindow = new Debugger_MemoryTex(this); - displaylistWindow = new Debugger_DisplayList(currentDebugMIPS, this, this); - - if (dialogDisasm) - dialogDisasm->NotifyMapLoaded(); - if (memoryWindow) - memoryWindow->NotifyMapLoaded(); - - if (nextState == CORE_RUNNING) - on_action_EmulationRun_triggered(); - UpdateMenus(); -} - -void MainWindow::CoreEmitWait(bool isWaiting) -{ - // Unlock mutex while core is waiting; - EmuThread_LockDraw(!isWaiting); + __CtrlSetAnalog(input_state.pad_lstick_x, input_state.pad_lstick_y); } void MainWindow::UpdateMenus() { - ui->action_OptionsDisplayRawFramebuffer->setChecked(g_Config.bDisplayFramebuffer); - ui->action_OptionsIgnoreIllegalReadsWrites->setChecked(g_Config.bIgnoreBadMemAccess); - ui->action_CPUInterpreter->setChecked(!g_Config.bJit); - ui->action_CPUDynarec->setChecked(g_Config.bJit); - ui->action_OptionsBufferedRendering->setChecked(g_Config.bBufferedRendering); - ui->action_OptionsShowDebugStatistics->setChecked(g_Config.bShowDebugStats); - ui->action_OptionsWireframe->setChecked(g_Config.bDrawWireframe); - ui->action_OptionsHardwareTransform->setChecked(g_Config.bHardwareTransform); - ui->action_OptionsFastMemory->setChecked(g_Config.bFastMemory); - ui->action_OptionsLinearFiltering->setChecked(g_Config.bLinearFiltering); - ui->action_EmulationRunLoad->setChecked(g_Config.bAutoRun); - ui->action_OptionsUseVBO->setChecked(g_Config.bUseVBO); - ui->action_OptionsVertexCache->setChecked(g_Config.bVertexCache); - ui->action_AFOff->setChecked(g_Config.iAnisotropyLevel == 0); - ui->action_AF2x->setChecked(g_Config.iAnisotropyLevel == 2); - ui->action_AF4x->setChecked(g_Config.iAnisotropyLevel == 4); - ui->action_AF8x->setChecked(g_Config.iAnisotropyLevel == 8); - ui->action_AF16x->setChecked(g_Config.iAnisotropyLevel == 16); - ui->action_Simple_2xAA->setChecked(g_Config.SSAntiAliasing); - ui->action_Show_FPS_counter->setChecked(g_Config.bShowFPSCounter); - ui->action_Stretch_to_display->setChecked(g_Config.bStretchToDisplay); - ui->action_Sound->setChecked(g_Config.bEnableSound); - - bool enable = !Core_IsStepping() ? false : true; - ui->action_EmulationRun->setEnabled(g_State.bEmuThreadStarted ? enable : false); - ui->action_EmulationPause->setEnabled(g_State.bEmuThreadStarted ? !enable : false); - ui->action_EmulationReset->setEnabled(g_State.bEmuThreadStarted ? true : false); - - enable = g_State.bEmuThreadStarted ? false : true; + // enabling + bool enable = g_State.bEmuThreadStarted ? false : true; ui->action_FileLoad->setEnabled(enable); + ui->action_FileClose->setEnabled(!enable); ui->action_FileSaveStateFile->setEnabled(!enable); ui->action_FileLoadStateFile->setEnabled(!enable); ui->action_FileQuickloadState->setEnabled(!enable); ui->action_FileQuickSaveState->setEnabled(!enable); ui->action_CPUDynarec->setEnabled(enable); ui->action_CPUInterpreter->setEnabled(enable); - ui->action_EmulationStop->setEnabled(!enable); ui->action_DebugDumpFrame->setEnabled(!enable); ui->action_DebugDisassembly->setEnabled(!enable); ui->action_DebugMemoryView->setEnabled(!enable); ui->action_DebugMemoryViewTexture->setEnabled(!enable); ui->action_DebugDisplayList->setEnabled(!enable); + enable = !Core_IsStepping() ? false : true; + ui->action_EmulationRun->setEnabled(g_State.bEmuThreadStarted ? enable : false); + ui->action_EmulationPause->setEnabled(g_State.bEmuThreadStarted ? !enable : false); + ui->action_EmulationReset->setEnabled(g_State.bEmuThreadStarted ? true : false); + + // checking + ui->action_EmulationRunLoad->setChecked(g_Config.bAutoRun); + + ui->action_CPUInterpreter->setChecked(!g_Config.bJit); + ui->action_CPUDynarec->setChecked(g_Config.bJit); + ui->action_OptionsFastMemory->setChecked(g_Config.bFastMemory); + ui->action_OptionsIgnoreIllegalReadsWrites->setChecked(g_Config.bIgnoreBadMemAccess); + + ui->action_AFOff->setChecked(g_Config.iAnisotropyLevel == 0); + ui->action_AF2x->setChecked(g_Config.iAnisotropyLevel == 2); + ui->action_AF4x->setChecked(g_Config.iAnisotropyLevel == 4); + ui->action_AF8x->setChecked(g_Config.iAnisotropyLevel == 8); + ui->action_AF16x->setChecked(g_Config.iAnisotropyLevel == 16); + + ui->action_OptionsBufferedRendering->setChecked(g_Config.bBufferedRendering); + ui->action_OptionsLinearFiltering->setChecked(g_Config.bLinearFiltering); + ui->action_Simple_2xAA->setChecked(g_Config.SSAntiAliasing); + ui->action_OptionsScreen1x->setChecked(0 == (g_Config.iWindowZoom - 1)); ui->action_OptionsScreen2x->setChecked(1 == (g_Config.iWindowZoom - 1)); ui->action_OptionsScreen3x->setChecked(2 == (g_Config.iWindowZoom - 1)); ui->action_OptionsScreen4x->setChecked(3 == (g_Config.iWindowZoom - 1)); + ui->action_Stretch_to_display->setChecked(g_Config.bStretchToDisplay); + ui->action_OptionsHardwareTransform->setChecked(g_Config.bHardwareTransform); + ui->action_OptionsUseVBO->setChecked(g_Config.bUseVBO); + ui->action_OptionsVertexCache->setChecked(g_Config.bVertexCache); + ui->action_OptionsWireframe->setChecked(g_Config.bDrawWireframe); + ui->action_OptionsDisplayRawFramebuffer->setChecked(g_Config.bDisplayFramebuffer); + ui->actionFrameskip->setChecked(g_Config.iFrameSkip != 0); + + ui->action_Sound->setChecked(g_Config.bEnableSound); + + ui->action_OptionsShowDebugStatistics->setChecked(g_Config.bShowDebugStats); + ui->action_Show_FPS_counter->setChecked(g_Config.bShowFPSCounter); + + ui->actionLogDefDebug->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LDEBUG); + ui->actionLogDefInfo->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LINFO); + ui->actionLogDefWarning->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LWARNING); + ui->actionLogDefError->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LERROR); + ui->actionLogG3DDebug->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::G3D) == LogTypes::LDEBUG); ui->actionLogG3DInfo->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::G3D) == LogTypes::LINFO); ui->actionLogG3DWarning->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::G3D) == LogTypes::LWARNING); @@ -286,71 +265,78 @@ void MainWindow::UpdateMenus() ui->actionLogHLEInfo->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::HLE) == LogTypes::LINFO); ui->actionLogHLEWarning->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::HLE) == LogTypes::LWARNING); ui->actionLogHLEError->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::HLE) == LogTypes::LERROR); +} - ui->actionLogDefDebug->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LDEBUG); - ui->actionLogDefInfo->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LINFO); - ui->actionLogDefWarning->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LWARNING); - ui->actionLogDefError->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LERROR); +void MainWindow::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) + ui->retranslateUi(this); } -void MainWindow::SetZoom(float zoom) { - if (zoom < 5) - g_Config.iWindowZoom = (int) zoom; +void MainWindow::closeEvent(QCloseEvent *) +{ + on_action_FileExit_triggered(); +} - pixel_xres = 480 * zoom; - pixel_yres = 272 * zoom; - dp_xres = pixel_xres; - dp_yres = pixel_yres; +void MainWindow::keyPressEvent(QKeyEvent *e) +{ + if(isFullScreen() && e->key() == Qt::Key_F12) + { + on_action_OptionsFullScreen_triggered(); + return; + } - emugl->resize(pixel_xres, pixel_yres); - emugl->setMinimumSize(pixel_xres, pixel_yres); - emugl->setMaximumSize(pixel_xres, pixel_yres); + pressedKeys.insert(e->key()); +} - ui->centralwidget->setFixedSize(pixel_xres, pixel_yres); - ui->centralwidget->resize(pixel_xres, pixel_yres); +void MainWindow::keyReleaseEvent(QKeyEvent *e) +{ + pressedKeys.remove(e->key()); +} - setFixedSize(sizeHint()); - resize(sizeHint()); +/* SLOTS */ +void MainWindow::Boot() +{ + dialogDisasm = new Debugger_Disasm(currentDebugMIPS, this, this); + if(g_Config.bShowDebuggerOnLoad) + dialogDisasm->show(); - PSP_CoreParameter().pixelWidth = pixel_xres; - PSP_CoreParameter().pixelHeight = pixel_yres; - PSP_CoreParameter().outputWidth = pixel_xres; - PSP_CoreParameter().outputHeight = pixel_yres; + if(g_Config.bFullScreen != isFullScreen()) + on_action_OptionsFullScreen_triggered(); - if (g_Config.SSAntiAliasing) - { - zoom *= 2; - PSP_CoreParameter().renderWidth = 480 * zoom; - PSP_CoreParameter().renderHeight = 272 * zoom; - } + memoryWindow = new Debugger_Memory(currentDebugMIPS, this, this); + memoryTexWindow = new Debugger_MemoryTex(this); + displaylistWindow = new Debugger_DisplayList(currentDebugMIPS, this, this); - if (gpu) - gpu->Resized(); + notifyMapsLoaded(); + + if (nextState == CORE_RUNNING) + on_action_EmulationRun_triggered(); + UpdateMenus(); } -void MainWindow::on_action_FileLoad_triggered() +void MainWindow::CoreEmitWait(bool isWaiting) { - BrowseAndBoot(); + // Unlock mutex while core is waiting; + EmuThread_LockDraw(!isWaiting); } -void MainWindow::on_action_EmulationRun_triggered() +void MainWindow::on_action_FileLoad_triggered() { - if (g_State.bEmuThreadStarted) + QString filename = QFileDialog::getOpenFileName(NULL, "Load File", g_Config.currentDirectory.c_str(), "PSP ROMs (*.pbp *.elf *.iso *.cso *.prx)"); + if (QFile::exists(filename)) { - if(dialogDisasm) - { - dialogDisasm->Stop(); - dialogDisasm->Go(); - } + QFileInfo info(filename); + g_Config.currentDirectory = info.absolutePath().toStdString(); + EmuThread_StartGame(filename); } } -void MainWindow::on_action_EmulationStop_triggered() +void MainWindow::on_action_FileClose_triggered() { if(dialogDisasm) - { dialogDisasm->Stop(); - } + // This will wait for ppsspp to pause EmuThread_LockDraw(true); EmuThread_LockDraw(false); @@ -369,12 +355,6 @@ void MainWindow::on_action_EmulationStop_triggered() UpdateMenus(); } -void MainWindow::on_action_EmulationPause_triggered() -{ - if(dialogDisasm) - dialogDisasm->Stop(); -} - void SaveStateActionFinished(bool result, void *userdata) { // TODO: Improve messaging? @@ -386,149 +366,98 @@ void SaveStateActionFinished(bool result, void *userdata) msgBox.exec(); return; } +} - MainWindow* mainWindow = (MainWindow*)userdata; +void MainWindow::on_action_FileQuickloadState_triggered() +{ + SaveState::LoadSlot(0, SaveStateActionFinished, this); +} - if (g_State.bEmuThreadStarted && mainWindow->GetNextState() == CORE_RUNNING) - { - if(mainWindow->GetDialogDisasm()) - mainWindow->GetDialogDisasm()->Go(); - } +void MainWindow::on_action_FileQuickSaveState_triggered() +{ + SaveState::SaveSlot(0, SaveStateActionFinished, this); } void MainWindow::on_action_FileLoadStateFile_triggered() { - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - if(dialogDisasm) - { - dialogDisasm->Stop(); - } - } QFileDialog dialog(0,"Load state"); dialog.setFileMode(QFileDialog::ExistingFile); QStringList filters; filters << "Save States (*.ppst)" << "|All files (*.*)"; dialog.setNameFilters(filters); dialog.setAcceptMode(QFileDialog::AcceptOpen); - QStringList fileNames; if (dialog.exec()) { - fileNames = dialog.selectedFiles(); + QStringList fileNames = dialog.selectedFiles(); SaveState::Load(fileNames[0].toStdString(), SaveStateActionFinished, this); } } - void MainWindow::on_action_FileSaveStateFile_triggered() { - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - if(dialogDisasm) - { - dialogDisasm->Stop(); - } - } QFileDialog dialog(0,"Save state"); dialog.setFileMode(QFileDialog::AnyFile); dialog.setAcceptMode(QFileDialog::AcceptSave); QStringList filters; filters << "Save States (*.ppst)" << "|All files (*.*)"; dialog.setNameFilters(filters); - QStringList fileNames; if (dialog.exec()) { - fileNames = dialog.selectedFiles(); + QStringList fileNames = dialog.selectedFiles(); SaveState::Save(fileNames[0].toStdString(), SaveStateActionFinished, this); } } -void MainWindow::on_action_FileQuickloadState_triggered() +void MainWindow::on_action_FileExit_triggered() { - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - if(dialogDisasm) - { - dialogDisasm->Stop(); - } - } - SaveState::LoadSlot(0, SaveStateActionFinished, this); + on_action_FileClose_triggered(); + EmuThread_Stop(); + QApplication::exit(0); } -void MainWindow::on_action_FileQuickSaveState_triggered() +void MainWindow::on_action_EmulationRun_triggered() { if (g_State.bEmuThreadStarted) { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; if(dialogDisasm) { dialogDisasm->Stop(); + dialogDisasm->Go(); } } - SaveState::SaveSlot(0, SaveStateActionFinished, this); } -void MainWindow::on_action_OptionsScreen1x_triggered() -{ - SetZoom(1); - UpdateMenus(); -} - -void MainWindow::on_action_OptionsScreen2x_triggered() -{ - SetZoom(2); - UpdateMenus(); -} - -void MainWindow::on_action_OptionsScreen3x_triggered() -{ - SetZoom(3); - UpdateMenus(); -} - -void MainWindow::on_action_OptionsScreen4x_triggered() +void MainWindow::on_action_EmulationPause_triggered() { - SetZoom(4); - UpdateMenus(); + if(dialogDisasm) + dialogDisasm->Stop(); } -void MainWindow::on_action_OptionsBufferedRendering_triggered() +void MainWindow::on_action_EmulationReset_triggered() { - g_Config.bBufferedRendering = !g_Config.bBufferedRendering; - UpdateMenus(); -} + if(dialogDisasm) + dialogDisasm->Stop(); -void MainWindow::on_action_OptionsShowDebugStatistics_triggered() -{ - g_Config.bShowDebugStats = !g_Config.bShowDebugStats; - UpdateMenus(); -} + EmuThread_LockDraw(true); + EmuThread_LockDraw(false); -void MainWindow::on_action_OptionsHardwareTransform_triggered() -{ - g_Config.bHardwareTransform = !g_Config.bHardwareTransform; - UpdateMenus(); -} + if(dialogDisasm) + dialogDisasm->close(); + if(memoryWindow) + memoryWindow->close(); + if(memoryTexWindow) + memoryTexWindow->close(); + if(displaylistWindow) + displaylistWindow->close(); -void MainWindow::on_action_FileExit_triggered() -{ - on_action_EmulationStop_triggered(); - EmuThread_Stop(); - QApplication::exit(0); -} + EmuThread_StopGame(); -void MainWindow::on_action_CPUDynarec_triggered() -{ - g_Config.bJit = true; - UpdateMenus(); + EmuThread_StartGame(GetCurrentFilename()); } -void MainWindow::on_action_CPUInterpreter_triggered() +void MainWindow::on_action_EmulationRunLoad_triggered() { - g_Config.bJit = false; + g_Config.bAutoRun = !g_Config.bAutoRun; UpdateMenus(); } @@ -545,10 +474,7 @@ void MainWindow::on_action_DebugLoadMapFile_triggered() { fileNames = dialog.selectedFiles(); symbolMap.LoadSymbolMap(fileNames[0].toStdString().c_str()); - if (dialogDisasm) - dialogDisasm->NotifyMapLoaded(); - if (memoryWindow) - memoryWindow->NotifyMapLoaded(); + notifyMapsLoaded(); } } @@ -571,10 +497,12 @@ void MainWindow::on_action_DebugSaveMapFile_triggered() void MainWindow::on_action_DebugResetSymbolTable_triggered() { symbolMap.ResetSymbolMap(); - if (dialogDisasm) - dialogDisasm->NotifyMapLoaded(); - if (memoryWindow) - memoryWindow->NotifyMapLoaded(); + notifyMapsLoaded(); +} + +void MainWindow::on_action_DebugDumpFrame_triggered() +{ + gpu->DumpNextFrame(); } void MainWindow::on_action_DebugDisassembly_triggered() @@ -583,10 +511,10 @@ void MainWindow::on_action_DebugDisassembly_triggered() dialogDisasm->show(); } -void MainWindow::on_action_DebugMemoryView_triggered() +void MainWindow::on_action_DebugDisplayList_triggered() { - if (memoryWindow) - memoryWindow->show(); + if(displaylistWindow) + displaylistWindow->show(); } void MainWindow::on_action_DebugLog_triggered() @@ -594,64 +522,27 @@ void MainWindow::on_action_DebugLog_triggered() LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); } -void MainWindow::on_action_OptionsIgnoreIllegalReadsWrites_triggered() +void MainWindow::on_action_DebugMemoryView_triggered() { - g_Config.bIgnoreBadMemAccess = !g_Config.bIgnoreBadMemAccess; - UpdateMenus(); + if (memoryWindow) + memoryWindow->show(); } -void MainWindow::on_action_OptionsFullScreen_triggered() +void MainWindow::on_action_DebugMemoryViewTexture_triggered() { - if(isFullScreen()) { - g_Config.bFullScreen = false; - showNormal(); - ui->menubar->setVisible(true); - ui->statusbar->setVisible(true); - SetZoom(g_Config.iWindowZoom); - } - else { - g_Config.bFullScreen = true; - ui->menubar->setVisible(false); - ui->statusbar->setVisible(false); - - // Remove constraint - emugl->setMinimumSize(0, 0); - emugl->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - ui->centralwidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - - showFullScreen(); - - int width = (int) QApplication::desktop()->screenGeometry().width(); - int height = (int) QApplication::desktop()->screenGeometry().height(); - PSP_CoreParameter().pixelWidth = width; - PSP_CoreParameter().pixelHeight = height; - PSP_CoreParameter().outputWidth = width; - PSP_CoreParameter().outputHeight = height; - - int antialias = 1; - if (g_Config.SSAntiAliasing) antialias = 2; - PSP_CoreParameter().renderWidth = width * antialias; - PSP_CoreParameter().renderHeight = height * antialias; - - pixel_xres = width; - pixel_yres = height; - dp_xres = pixel_xres; - dp_yres = pixel_yres; - if (gpu) - gpu->Resized(); - } + if(memoryTexWindow) + memoryTexWindow->show(); } -void MainWindow::on_action_OptionsWireframe_triggered() +void MainWindow::on_action_CPUDynarec_triggered() { - g_Config.bDrawWireframe = !g_Config.bDrawWireframe; + g_Config.bJit = true; UpdateMenus(); } -void MainWindow::on_action_OptionsDisplayRawFramebuffer_triggered() +void MainWindow::on_action_CPUInterpreter_triggered() { - g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer; + g_Config.bJit = false; UpdateMenus(); } @@ -661,9 +552,9 @@ void MainWindow::on_action_OptionsFastMemory_triggered() UpdateMenus(); } -void MainWindow::on_action_OptionsLinearFiltering_triggered() +void MainWindow::on_action_OptionsIgnoreIllegalReadsWrites_triggered() { - g_Config.bLinearFiltering = !g_Config.bLinearFiltering; + g_Config.bIgnoreBadMemAccess = !g_Config.bIgnoreBadMemAccess; UpdateMenus(); } @@ -672,372 +563,401 @@ void MainWindow::on_action_OptionsControls_triggered() controls->show(); } -void MainWindow::on_action_HelpOpenWebsite_triggered() +void MainWindow::on_action_OptionsGamePadControls_triggered() { - QDesktopServices::openUrl(QUrl("http://www.ppsspp.org/")); +#if QT_HAS_SDL + gamePadDlg->show(); +#else + QMessageBox::information(this,tr("Gamepad"),tr("You need to compile with SDL to have Gamepad support."), QMessageBox::Ok); +#endif } -void MainWindow::on_action_HelpAbout_triggered() +void MainWindow::on_action_AFOff_triggered() { - // TODO display about + g_Config.iAnisotropyLevel = 0; + UpdateMenus(); } -void MainWindow::closeEvent(QCloseEvent *event) +void MainWindow::on_action_AF2x_triggered() { - on_action_FileExit_triggered(); + g_Config.iAnisotropyLevel = 2; + UpdateMenus(); } -void MainWindow::keyPressEvent(QKeyEvent *e) +void MainWindow::on_action_AF4x_triggered() { - if(isFullScreen() && e->key() == Qt::Key_F12) - { - on_action_OptionsFullScreen_triggered(); - return; - } - - pressedKeys.insert(e->key()); + g_Config.iAnisotropyLevel = 4; + UpdateMenus(); } -void MainWindow::keyReleaseEvent(QKeyEvent *e) +void MainWindow::on_action_AF8x_triggered() { - pressedKeys.remove(e->key()); + g_Config.iAnisotropyLevel = 8; + UpdateMenus(); } -void MainWindow::on_MainWindow_destroyed() +void MainWindow::on_action_AF16x_triggered() { - + g_Config.iAnisotropyLevel = 16; + UpdateMenus(); } -void MainWindow::on_actionLogG3DDebug_triggered() +void MainWindow::on_action_OptionsBufferedRendering_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LDEBUG); + g_Config.bBufferedRendering = !g_Config.bBufferedRendering; UpdateMenus(); } -void MainWindow::on_actionLogG3DWarning_triggered() +void MainWindow::on_action_OptionsLinearFiltering_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LWARNING); + g_Config.bLinearFiltering = !g_Config.bLinearFiltering; UpdateMenus(); } -void MainWindow::on_actionLogG3DError_triggered() +void MainWindow::on_action_Simple_2xAA_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LERROR); + g_Config.SSAntiAliasing = !g_Config.SSAntiAliasing; UpdateMenus(); } -void MainWindow::on_actionLogG3DInfo_triggered() +void MainWindow::on_action_OptionsScreen1x_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LINFO); + SetZoom(1); UpdateMenus(); } -void MainWindow::on_actionLogHLEDebug_triggered() +void MainWindow::on_action_OptionsScreen2x_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LDEBUG); + SetZoom(2); UpdateMenus(); } -void MainWindow::on_actionLogHLEWarning_triggered() +void MainWindow::on_action_OptionsScreen3x_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LWARNING); + SetZoom(3); UpdateMenus(); } -void MainWindow::on_actionLogHLEInfo_triggered() +void MainWindow::on_action_OptionsScreen4x_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LINFO); + SetZoom(4); UpdateMenus(); } -void MainWindow::on_actionLogHLEError_triggered() +void MainWindow::on_action_Stretch_to_display_triggered() { - LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LERROR); + g_Config.bStretchToDisplay = !g_Config.bStretchToDisplay; UpdateMenus(); + if (gpu) + gpu->Resized(); } -void MainWindow::on_actionLogDefDebug_triggered() +void MainWindow::on_action_OptionsHardwareTransform_triggered() { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; - if(type == LogTypes::G3D || type == LogTypes::HLE) continue; - LogManager::GetInstance()->SetLogLevel(type, LogTypes::LDEBUG); - } + g_Config.bHardwareTransform = !g_Config.bHardwareTransform; UpdateMenus(); } -void MainWindow::on_actionLogDefWarning_triggered() +void MainWindow::on_action_OptionsUseVBO_triggered() { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; - if(type == LogTypes::G3D || type == LogTypes::HLE) continue; - LogManager::GetInstance()->SetLogLevel(type, LogTypes::LWARNING); - } + g_Config.bUseVBO = !g_Config.bUseVBO; UpdateMenus(); } -void MainWindow::on_actionLogDefInfo_triggered() +void MainWindow::on_action_OptionsVertexCache_triggered() { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; - if(type == LogTypes::G3D || type == LogTypes::HLE) continue; - LogManager::GetInstance()->SetLogLevel(type, LogTypes::LINFO); - } + g_Config.bVertexCache = !g_Config.bVertexCache; UpdateMenus(); } -void MainWindow::on_actionLogDefError_triggered() +void MainWindow::on_action_OptionsWireframe_triggered() { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; - if(type == LogTypes::G3D || type == LogTypes::HLE) continue; - LogManager::GetInstance()->SetLogLevel(type, LogTypes::LERROR); - } + g_Config.bDrawWireframe = !g_Config.bDrawWireframe; UpdateMenus(); } -void MainWindow::on_action_OptionsGamePadControls_triggered() +void MainWindow::on_action_OptionsDisplayRawFramebuffer_triggered() { -#if QT_HAS_SDL - gamePadDlg->show(); -#else - QMessageBox::information(this,"Gamepad","You need to compile with SDL to have Gamepad support.", QMessageBox::Ok); -#endif + g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer; + UpdateMenus(); } -void MainWindow::on_language_changed(QAction *action) +void MainWindow::on_actionFrameskip_triggered() { - if (0 != action) - { - loadLanguage(action->data().toString()); - } + g_Config.iFrameSkip = !g_Config.iFrameSkip; + UpdateMenus(); } -void switchTranslator(QTranslator &translator, const QString &filename) +void MainWindow::on_action_Sound_triggered() { - qApp->removeTranslator(&translator); - - if (translator.load(filename)) - qApp->installTranslator(&translator); + g_Config.bEnableSound = !g_Config.bEnableSound; + UpdateMenus(); } -void MainWindow::loadLanguage(const QString& language) +void MainWindow::on_action_OptionsFullScreen_triggered() { - if (currentLanguage != language) - { - currentLanguage = language; - QLocale locale = QLocale(currentLanguage); - QLocale::setDefault(locale); - QString languageName = QLocale::languageToString(locale.language()); - switchTranslator(translator, QString(":/languages/ppsspp_%1.qm").arg(language)); + if(isFullScreen()) { + g_Config.bFullScreen = false; + showNormal(); + ui->menubar->setVisible(true); + ui->statusbar->setVisible(true); + SetZoom(g_Config.iWindowZoom); } -} - -void MainWindow::createLanguageMenu() -{ - QActionGroup *langGroup = new QActionGroup(ui->menuLanguage); - langGroup->setExclusive(true); - - connect(langGroup, SIGNAL(triggered(QAction *)), this, SLOT(on_language_changed(QAction *))); - - QString defaultLocale = QLocale::system().name(); - defaultLocale.truncate(defaultLocale.lastIndexOf('_')); - QStringList fileNames = QDir(":/languages").entryList(QStringList("ppsspp_*.qm")); + else { + g_Config.bFullScreen = true; + ui->menubar->setVisible(false); + ui->statusbar->setVisible(false); - if (fileNames.size() == 0) - { - QAction *action = new QAction(tr("No translations"), this); - action->setCheckable(false); - action->setDisabled(true); - ui->menuLanguage->addAction(action); - langGroup->addAction(action); - } + // Remove constraint + emugl->setMinimumSize(0, 0); + emugl->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + ui->centralwidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - for (int i = 0; i < fileNames.size(); ++i) - { - QString locale = fileNames[i]; - locale.truncate(locale.lastIndexOf('.')); - locale.remove(0, locale.indexOf('_') + 1); + showFullScreen(); -#if QT_VERSION >= 0x040800 - QString language = QLocale(locale).nativeLanguageName(); -#else - QString language = QLocale::languageToString(QLocale(locale).language()); -#endif - QAction *action = new QAction(language, this); - action->setCheckable(true); - action->setData(locale); + int width = (int) QApplication::desktop()->screenGeometry().width(); + int height = (int) QApplication::desktop()->screenGeometry().height(); + PSP_CoreParameter().pixelWidth = width; + PSP_CoreParameter().pixelHeight = height; + PSP_CoreParameter().outputWidth = width; + PSP_CoreParameter().outputHeight = height; - ui->menuLanguage->addAction(action); - langGroup->addAction(action); + int antialias = 1; + if (g_Config.SSAntiAliasing) antialias = 2; + PSP_CoreParameter().renderWidth = width * antialias; + PSP_CoreParameter().renderHeight = height * antialias; - // TODO check en as default until we save language to config - if ("en" == locale) - { - action->setChecked(true); - currentLanguage = "en"; - } + pixel_xres = width; + pixel_yres = height; + dp_xres = pixel_xres; + dp_yres = pixel_yres; + if (gpu) + gpu->Resized(); } } -void MainWindow::changeEvent(QEvent *event) +void MainWindow::on_action_OptionsShowDebugStatistics_triggered() { - QMainWindow::changeEvent(event); - - if (0 != event) - { - switch (event->type()) - { - case QEvent::LanguageChange: - ui->retranslateUi(this); - break; - case QEvent::LocaleChange: - { - QString locale = QLocale::system().name(); - locale.truncate(locale.lastIndexOf('_')); - loadLanguage(locale); - } - break; - default: - break; - } - } + g_Config.bShowDebugStats = !g_Config.bShowDebugStats; + UpdateMenus(); } -void MainWindow::ShowMemory(u32 addr) +void MainWindow::on_action_Show_FPS_counter_triggered() { - if(memoryWindow) - memoryWindow->Goto(addr); + g_Config.bShowFPSCounter = !g_Config.bShowFPSCounter; + UpdateMenus(); } -void MainWindow::Update() +void setDefLogLevel(LogTypes::LOG_LEVELS level) { - UpdateInputState(&input_state); - - for (int i = 0; i < controllistCount; i++) + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) { - if (pressedKeys.contains(controllist[i].key) || - input_state.pad_buttons_down & controllist[i].emu_id) - __CtrlButtonDown(controllist[i].psp_id); - else - __CtrlButtonUp(controllist[i].psp_id); + LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i; + if(type == LogTypes::G3D || type == LogTypes::HLE) continue; + LogManager::GetInstance()->SetLogLevel(type, level); } - __CtrlSetAnalog(input_state.pad_lstick_x, input_state.pad_lstick_y); } -void MainWindow::on_action_EmulationReset_triggered() +void MainWindow::on_actionLogDefDebug_triggered() { - if(dialogDisasm) - dialogDisasm->Stop(); - - EmuThread_LockDraw(true); - EmuThread_LockDraw(false); - - if(dialogDisasm) - dialogDisasm->close(); - if(memoryWindow) - memoryWindow->close(); - if(memoryTexWindow) - memoryTexWindow->close(); - if(displaylistWindow) - displaylistWindow->close(); - - EmuThread_StopGame(); - - EmuThread_StartGame(GetCurrentFilename()); + setDefLogLevel(LogTypes::LDEBUG); + UpdateMenus(); } -void MainWindow::on_action_DebugDumpFrame_triggered() +void MainWindow::on_actionLogDefWarning_triggered() { - gpu->DumpNextFrame(); + setDefLogLevel(LogTypes::LWARNING); + UpdateMenus(); } -void MainWindow::on_action_EmulationRunLoad_triggered() +void MainWindow::on_actionLogDefInfo_triggered() { - g_Config.bAutoRun = !g_Config.bAutoRun; + setDefLogLevel(LogTypes::LINFO); UpdateMenus(); } -void MainWindow::on_action_OptionsVertexCache_triggered() +void MainWindow::on_actionLogDefError_triggered() { - g_Config.bVertexCache = !g_Config.bVertexCache; + setDefLogLevel(LogTypes::LERROR); UpdateMenus(); } -void MainWindow::on_action_OptionsUseVBO_triggered() +void MainWindow::on_actionLogG3DDebug_triggered() { - g_Config.bUseVBO = !g_Config.bUseVBO; + LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LDEBUG); UpdateMenus(); } -void MainWindow::on_action_Simple_2xAA_triggered() +void MainWindow::on_actionLogG3DWarning_triggered() { - g_Config.SSAntiAliasing = !g_Config.SSAntiAliasing; + LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LWARNING); UpdateMenus(); } -void MainWindow::on_action_AFOff_triggered() +void MainWindow::on_actionLogG3DError_triggered() { - g_Config.iAnisotropyLevel = 0; + LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LERROR); UpdateMenus(); } -void MainWindow::on_action_AF2x_triggered() +void MainWindow::on_actionLogG3DInfo_triggered() { - g_Config.iAnisotropyLevel = 2; + LogManager::GetInstance()->SetLogLevel(LogTypes::G3D, LogTypes::LINFO); UpdateMenus(); } -void MainWindow::on_action_AF4x_triggered() +void MainWindow::on_actionLogHLEDebug_triggered() { - g_Config.iAnisotropyLevel = 4; + LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LDEBUG); UpdateMenus(); } -void MainWindow::on_action_AF8x_triggered() +void MainWindow::on_actionLogHLEWarning_triggered() { - g_Config.iAnisotropyLevel = 8; + LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LWARNING); UpdateMenus(); } -void MainWindow::on_action_AF16x_triggered() +void MainWindow::on_actionLogHLEInfo_triggered() { - g_Config.iAnisotropyLevel = 16; + LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LINFO); UpdateMenus(); } -void MainWindow::on_action_Show_FPS_counter_triggered() +void MainWindow::on_actionLogHLEError_triggered() { - g_Config.bShowFPSCounter = !g_Config.bShowFPSCounter; + LogManager::GetInstance()->SetLogLevel(LogTypes::HLE, LogTypes::LERROR); UpdateMenus(); } -void MainWindow::on_action_Stretch_to_display_triggered() +void MainWindow::on_action_HelpOpenWebsite_triggered() { - g_Config.bStretchToDisplay = !g_Config.bStretchToDisplay; - UpdateMenus(); + QDesktopServices::openUrl(QUrl("http://www.ppsspp.org/")); +} + +void MainWindow::on_action_HelpAbout_triggered() +{ + // TODO display about +} + +void MainWindow::on_language_changed(QAction *action) +{ + loadLanguage(action->data().toString()); +} + +/* Private functions */ +void MainWindow::SetZoom(float zoom) { + if (zoom < 5) + g_Config.iWindowZoom = (int) zoom; + + pixel_xres = 480 * zoom; + pixel_yres = 272 * zoom; + dp_xres = pixel_xres; + dp_yres = pixel_yres; + + emugl->resize(pixel_xres, pixel_yres); + emugl->setMinimumSize(pixel_xres, pixel_yres); + emugl->setMaximumSize(pixel_xres, pixel_yres); + + ui->centralwidget->setFixedSize(pixel_xres, pixel_yres); + ui->centralwidget->resize(pixel_xres, pixel_yres); + + setFixedSize(sizeHint()); + resize(sizeHint()); + + PSP_CoreParameter().pixelWidth = pixel_xres; + PSP_CoreParameter().pixelHeight = pixel_yres; + PSP_CoreParameter().outputWidth = pixel_xres; + PSP_CoreParameter().outputHeight = pixel_yres; + + if (g_Config.SSAntiAliasing) + { + zoom *= 2; + PSP_CoreParameter().renderWidth = 480 * zoom; + PSP_CoreParameter().renderHeight = 272 * zoom; + } + if (gpu) gpu->Resized(); } -void MainWindow::on_action_Sound_triggered() +void MainWindow::SetGameTitle(QString text) { - g_Config.bEnableSound = !g_Config.bEnableSound; - UpdateMenus(); + QString title = "PPSSPP " + QString(PPSSPP_GIT_VERSION); + if (text != "") + title += QString(" - %1").arg(text); + + setWindowTitle(title); } -void MainWindow::on_action_DebugMemoryViewTexture_triggered() +void switchTranslator(QTranslator &translator, const QString &filename) { - if(memoryTexWindow) - memoryTexWindow->show(); + qApp->removeTranslator(&translator); + + if (translator.load(filename)) + qApp->installTranslator(&translator); } -void MainWindow::on_action_DebugDisplayList_triggered() +void MainWindow::loadLanguage(const QString& language) { - if(displaylistWindow) - displaylistWindow->show(); + if (currentLanguage != language) + { + currentLanguage = language; + QLocale::setDefault(QLocale(currentLanguage)); + switchTranslator(translator, QString(":/languages/ppsspp_%1.qm").arg(language)); + } +} + +void MainWindow::createLanguageMenu() +{ + QActionGroup *langGroup = new QActionGroup(ui->menuLanguage); + langGroup->setExclusive(true); + + connect(langGroup, SIGNAL(triggered(QAction *)), this, SLOT(on_language_changed(QAction *))); + + QStringList fileNames = QDir(":/languages").entryList(QStringList("ppsspp_*.qm")); + + if (fileNames.size() == 0) + { + QAction *action = new QAction(tr("No translations"), this); + action->setCheckable(false); + action->setDisabled(true); + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + } + + for (int i = 0; i < fileNames.size(); ++i) + { + QString locale = fileNames[i]; + locale.truncate(locale.lastIndexOf('.')); + locale.remove(0, locale.indexOf('_') + 1); + +#if QT_VERSION >= 0x040800 + QString language = QLocale(locale).nativeLanguageName(); +#else + QString language = QLocale::languageToString(QLocale(locale).language()); +#endif + QAction *action = new QAction(language, this); + action->setCheckable(true); + action->setData(locale); + + ui->menuLanguage->addAction(action); + langGroup->addAction(action); + + // TODO check en as default until we save language to config + if ("en" == locale) + { + action->setChecked(true); + currentLanguage = "en"; + } + } +} + +void MainWindow::notifyMapsLoaded() +{ + if (dialogDisasm) + dialogDisasm->NotifyMapLoaded(); + if (memoryWindow) + memoryWindow->NotifyMapLoaded(); } diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index d2de2879276a..cb0af14bb0ef 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -24,182 +24,142 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - - void UpdateMenus(); - void SetZoom(float zoom); - void Create(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID); - void BrowseAndBoot(); - void SetNextState(CoreState state); - void SetGameTitle(QString text); + ~MainWindow(); Debugger_Disasm* GetDialogDisasm() { return dialogDisasm; } Debugger_Memory* GetDialogMemory() { return memoryWindow; } Debugger_MemoryTex* GetDialogMemoryTex() { return memoryTexWindow; } Debugger_DisplayList* GetDialogDisplaylist() { return displaylistWindow; } CoreState GetNextState() { return nextState; } - void closeEvent(QCloseEvent *event); - void keyPressEvent(QKeyEvent *); - void keyReleaseEvent(QKeyEvent *e); + void ShowMemory(u32 addr); void Update(); -public slots: + void UpdateMenus(); + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); +public slots: void Boot(); void CoreEmitWait(bool); private slots: + // File void on_action_FileLoad_triggered(); - - void on_action_EmulationRun_triggered(); - - void on_action_EmulationStop_triggered(); - - void on_action_EmulationPause_triggered(); - - void on_action_FileLoadStateFile_triggered(); - - void on_action_FileSaveStateFile_triggered(); - + void on_action_FileClose_triggered(); void on_action_FileQuickloadState_triggered(); - void on_action_FileQuickSaveState_triggered(); - - void on_action_OptionsScreen1x_triggered(); - - void on_action_OptionsScreen2x_triggered(); - - void on_action_OptionsScreen3x_triggered(); - - void on_action_OptionsScreen4x_triggered(); - - void on_action_OptionsBufferedRendering_triggered(); - - void on_action_OptionsShowDebugStatistics_triggered(); - - void on_action_OptionsHardwareTransform_triggered(); - + void on_action_FileLoadStateFile_triggered(); + void on_action_FileSaveStateFile_triggered(); void on_action_FileExit_triggered(); - void on_action_CPUDynarec_triggered(); - - void on_action_CPUInterpreter_triggered(); + // Emulation + void on_action_EmulationRun_triggered(); + void on_action_EmulationPause_triggered(); + void on_action_EmulationReset_triggered(); + void on_action_EmulationRunLoad_triggered(); + // Debug void on_action_DebugLoadMapFile_triggered(); - void on_action_DebugSaveMapFile_triggered(); - void on_action_DebugResetSymbolTable_triggered(); - + void on_action_DebugDumpFrame_triggered(); void on_action_DebugDisassembly_triggered(); - - void on_action_DebugMemoryView_triggered(); - + void on_action_DebugDisplayList_triggered(); void on_action_DebugLog_triggered(); + void on_action_DebugMemoryView_triggered(); + void on_action_DebugMemoryViewTexture_triggered(); + // Options + // Core + void on_action_CPUDynarec_triggered(); + void on_action_CPUInterpreter_triggered(); + void on_action_OptionsFastMemory_triggered(); void on_action_OptionsIgnoreIllegalReadsWrites_triggered(); - void on_action_OptionsFullScreen_triggered(); - - void on_action_OptionsWireframe_triggered(); - - void on_action_OptionsDisplayRawFramebuffer_triggered(); + // Controls + void on_action_OptionsControls_triggered(); + void on_action_OptionsGamePadControls_triggered(); - void on_action_OptionsFastMemory_triggered(); + // Video + void on_action_AFOff_triggered(); + void on_action_AF2x_triggered(); + void on_action_AF4x_triggered(); + void on_action_AF8x_triggered(); + void on_action_AF16x_triggered(); + void on_action_OptionsBufferedRendering_triggered(); void on_action_OptionsLinearFiltering_triggered(); + void on_action_Simple_2xAA_triggered(); - void on_action_OptionsControls_triggered(); + void on_action_OptionsScreen1x_triggered(); + void on_action_OptionsScreen2x_triggered(); + void on_action_OptionsScreen3x_triggered(); + void on_action_OptionsScreen4x_triggered(); - void on_action_HelpOpenWebsite_triggered(); + void on_action_Stretch_to_display_triggered(); + void on_action_OptionsHardwareTransform_triggered(); + void on_action_OptionsUseVBO_triggered(); + void on_action_OptionsVertexCache_triggered(); + void on_action_OptionsWireframe_triggered(); + void on_action_OptionsDisplayRawFramebuffer_triggered(); + void on_actionFrameskip_triggered(); - void on_action_HelpAbout_triggered(); + // Sound + void on_action_Sound_triggered(); - void on_MainWindow_destroyed(); + void on_action_OptionsFullScreen_triggered(); + void on_action_OptionsShowDebugStatistics_triggered(); + void on_action_Show_FPS_counter_triggered(); - void on_actionLogG3DDebug_triggered(); + // Logs + void on_actionLogDefDebug_triggered(); + void on_actionLogDefWarning_triggered(); + void on_actionLogDefInfo_triggered(); + void on_actionLogDefError_triggered(); + void on_actionLogG3DDebug_triggered(); void on_actionLogG3DWarning_triggered(); - void on_actionLogG3DError_triggered(); - void on_actionLogG3DInfo_triggered(); void on_actionLogHLEDebug_triggered(); - void on_actionLogHLEWarning_triggered(); - void on_actionLogHLEInfo_triggered(); - void on_actionLogHLEError_triggered(); - void on_actionLogDefDebug_triggered(); - - void on_actionLogDefWarning_triggered(); - - void on_actionLogDefInfo_triggered(); - - void on_actionLogDefError_triggered(); - - void on_action_OptionsGamePadControls_triggered(); + // Help + void on_action_HelpOpenWebsite_triggered(); + void on_action_HelpAbout_triggered(); + // Others void on_language_changed(QAction *action); - void on_action_EmulationReset_triggered(); - - void on_action_DebugDumpFrame_triggered(); - - void on_action_EmulationRunLoad_triggered(); - - void on_action_OptionsVertexCache_triggered(); - - void on_action_OptionsUseVBO_triggered(); - - void on_action_Simple_2xAA_triggered(); - - void on_action_AFOff_triggered(); - - void on_action_AF2x_triggered(); - - void on_action_AF4x_triggered(); - - void on_action_AF8x_triggered(); - - void on_action_AF16x_triggered(); - - void on_action_Show_FPS_counter_triggered(); - - void on_action_Stretch_to_display_triggered(); - - void on_action_Sound_triggered(); - - void on_action_DebugMemoryViewTexture_triggered(); - - void on_action_DebugDisplayList_triggered(); - private: + void SetZoom(float zoom); + void SetGameTitle(QString text); void loadLanguage(const QString &language); void createLanguageMenu(); - void changeEvent(QEvent *); + void notifyMapsLoaded(); QTranslator translator; - QTranslator qtTranslator; QString currentLanguage; - QString languagePath; Ui::MainWindow *ui; - QtEmuGL* emugl; + QtEmuGL *emugl; CoreState nextState; - InputState input_state; Debugger_Disasm *dialogDisasm; Debugger_Memory *memoryWindow; Debugger_MemoryTex *memoryTexWindow; Debugger_DisplayList *displaylistWindow; - Controls* controls; - GamePadDialog* gamePadDlg; + Controls *controls; + GamePadDialog *gamePadDlg; QSet pressedKeys; }; diff --git a/Qt/mainwindow.ui b/Qt/mainwindow.ui index a6ef9df025a5..6cabbd9838cc 100644 --- a/Qt/mainwindow.ui +++ b/Qt/mainwindow.ui @@ -44,7 +44,7 @@ 0 0 800 - 23 + 21 @@ -52,7 +52,7 @@ &File - + @@ -169,6 +169,7 @@ + @@ -220,7 +221,7 @@ &Open... - + &Close @@ -695,6 +696,14 @@ &Sound emulation + + + true + + + Frameskip + + diff --git a/Qt/resources.qrc b/Qt/resources.qrc index a6d411f82e87..b2e67dd43d82 100644 --- a/Qt/resources.qrc +++ b/Qt/resources.qrc @@ -22,5 +22,9 @@ languages/ppsspp_en.qm languages/ppsspp_fr.qm languages/ppsspp_pl.qm + languages/ppsspp_de.qm + languages/ppsspp_tc.qm + languages/ppsspp_pt-br.qm + languages/ppsspp_id.qm diff --git a/README.md b/README.md index 6131a815d850..7b644667cf12 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,6 @@ other than Microsoft's, but `NMake Makefiles` works fine. Building for iOS ---------------- -Note: This does not work yet. - Create a `build-ios` directory and inside it run: cmake -DCMAKE_TOOLCHAIN_FILE=../ios/ios.toolchain.cmake -GXcode .. diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 6692978d967a..7f0d044c5de0 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -66,11 +66,11 @@ CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Di tcItem.mask = TCIF_TEXT; tcItem.dwState = 0; tcItem.pszText = "Regs"; - tcItem.cchTextMax = strlen(tcItem.pszText)+1; + tcItem.cchTextMax = (int)strlen(tcItem.pszText)+1; tcItem.iImage = 0; int result1 = TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); tcItem.pszText = "Funcs"; - tcItem.cchTextMax = strlen(tcItem.pszText)+1; + tcItem.cchTextMax = (int)strlen(tcItem.pszText)+1; int result2 = TabCtrl_InsertItem(tabs, TabCtrl_GetItemCount(tabs),&tcItem); ShowWindow(GetDlgItem(m_hDlg, IDC_REGLIST), SW_NORMAL); ShowWindow(GetDlgItem(m_hDlg, IDC_FUNCTIONLIST), SW_HIDE); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index ac7e4fc1532f..ed65398786e3 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -79,7 +79,6 @@ DWORD TheThread(LPVOID x) coreParameter.pixelWidth = 480 * g_Config.iWindowZoom; coreParameter.pixelHeight = 272 * g_Config.iWindowZoom; coreParameter.startPaused = !g_Config.bAutoRun; - coreParameter.disableG3Dlog = g_Config.bDisableG3DLog; coreParameter.useMediaEngine = false; std::string error_string; diff --git a/Windows/OpenGLBase.cpp b/Windows/OpenGLBase.cpp index f739dcab1e68..92d4af4c14ec 100644 --- a/Windows/OpenGLBase.cpp +++ b/Windows/OpenGLBase.cpp @@ -49,14 +49,7 @@ void GL_Resized() // Resize And Initialize The GL Window glstate.viewport.restore(); } - -void GL_BeginFrame() -{ - -} - - -void GL_EndFrame() +void GL_SwapBuffers() { SwapBuffers(hDC); } diff --git a/Windows/OpenGLBase.h b/Windows/OpenGLBase.h index 50a154089f67..b26ed0ebfda6 100644 --- a/Windows/OpenGLBase.h +++ b/Windows/OpenGLBase.h @@ -2,9 +2,10 @@ #pragma once +#define WIN32_LEAN_AND_MEAN +#include bool GL_Init(HWND window); void GL_Shutdown(); void GL_Resized(); -void GL_BeginFrame(); -void GL_EndFrame(); +void GL_SwapBuffers(); diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 019fde5bccd0..04c498d664bf 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -339,6 +339,7 @@ + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index d2538d562b2e..7456a22f8a87 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -209,10 +209,13 @@ Android - Android + + Windows + + diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index f7afab484fe7..7f17e86137b4 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -38,9 +38,7 @@ void WindowsHost::ShutdownGL() void WindowsHost::SetWindowTitle(const char *message) { - // Really need a better way to deal with versions. - std::string title = "PPSSPP " PPSSPP_VERSION_STR " - "; - title += message; + std::string title = std::string("PPSSPP ") + PPSSPP_GIT_VERSION + " - " + message; int size = MultiByteToWideChar(CP_UTF8, 0, message, (int) title.size(), NULL, 0); if (size > 0) @@ -117,11 +115,6 @@ void WindowsHost::BeginFrame() for (auto iter = this->input.begin(); iter != this->input.end(); iter++) if ((*iter)->UpdateState() == 0) break; // *iter is std::shared_ptr, **iter is InputDevice - GL_BeginFrame(); -} -void WindowsHost::EndFrame() -{ - GL_EndFrame(); } void WindowsHost::BootDone() diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index c46c88854352..923acf10b8ca 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -21,7 +21,6 @@ class WindowsHost : public Host void InitGL(); void BeginFrame(); - void EndFrame(); void ShutdownGL(); void InitSound(PMixer *mixer); diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 2f3b07e531c7..b6b769f6559a 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -308,7 +308,7 @@ namespace MainWindow if (disasmWindow[i]) SendMessage(disasmWindow[i]->GetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - Sleep(100);//UGLY wait for event instead + Core_WaitInactive(); for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - Sleep(100);//UGLY wait for event instead + Core_WaitInactive(); for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - } if (W32Util::BrowseForFileName(true, hWnd, "Load state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn)) { SetCursor(LoadCursor(0,IDC_WAIT)); @@ -369,13 +362,6 @@ namespace MainWindow break; case ID_FILE_SAVESTATEFILE: - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - } if (W32Util::BrowseForFileName(false, hWnd, "Save state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn)) { SetCursor(LoadCursor(0,IDC_WAIT)); @@ -386,25 +372,11 @@ namespace MainWindow // TODO: Add UI for multiple slots case ID_FILE_QUICKLOADSTATE: - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - } SetCursor(LoadCursor(0,IDC_WAIT)); SaveState::LoadSlot(0, SaveStateActionFinished); break; case ID_FILE_QUICKSAVESTATE: - if (g_State.bEmuThreadStarted) - { - nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; - for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); - } SetCursor(LoadCursor(0,IDC_WAIT)); SaveState::SaveSlot(0, SaveStateActionFinished); break; @@ -429,6 +401,8 @@ namespace MainWindow case ID_OPTIONS_BUFFEREDRENDERING: g_Config.bBufferedRendering = !g_Config.bBufferedRendering; UpdateMenus(); + if (gpu) + gpu->Resized(); // easy way to force a clear... break; case ID_OPTIONS_SHOWDEBUGSTATISTICS: @@ -444,7 +418,13 @@ namespace MainWindow case ID_OPTIONS_STRETCHDISPLAY: g_Config.bStretchToDisplay = !g_Config.bStretchToDisplay; UpdateMenus(); - gpu->Resized(); // easy way to force a clear... + if (gpu) + gpu->Resized(); // easy way to force a clear... + break; + + case ID_OPTIONS_FRAMESKIP: + g_Config.iFrameSkip = !g_Config.iFrameSkip; + UpdateMenus(); break; case ID_FILE_EXIT: @@ -466,12 +446,12 @@ namespace MainWindow break; case ID_DEBUG_DUMPNEXTFRAME: - gpu->DumpNextFrame(); + if (gpu) + gpu->DumpNextFrame(); break; case ID_DEBUG_LOADMAPFILE: - if (W32Util::BrowseForFileName(true, hWnd, "Load .MAP",0,"Maps\0*.map\0All files\0*.*\0\0","map",fn)) - { + if (W32Util::BrowseForFileName(true, hWnd, "Load .MAP",0,"Maps\0*.map\0All files\0*.*\0\0","map",fn)) { symbolMap.LoadSymbolMap(fn.c_str()); // HLE_PatchFunctions(); for (int i=0; iSetEnable(LogTypes::G3D, true); - else - LogManager::GetInstance()->SetEnable(LogTypes::G3D, false); - UpdateMenus(); - break; case ID_OPTIONS_CONTROLS: DialogManager::EnableAll(FALSE); DialogBox(hInst, (LPCTSTR)IDD_CONTROLS, hWnd, (DLGPROC)Controls); @@ -638,7 +610,8 @@ namespace MainWindow break; case WM_CLOSE: - Sleep(100);//UGLY wait for event instead + Core_Stop(); + Core_WaitInactive(200); EmuThread_Stop(); /* @@ -709,9 +682,9 @@ namespace MainWindow CHECKITEM(ID_OPTIONS_STRETCHDISPLAY, g_Config.bStretchToDisplay); CHECKITEM(ID_EMULATION_RUNONLOAD, g_Config.bAutoRun); CHECKITEM(ID_OPTIONS_USEVBO, g_Config.bUseVBO); - CHECKITEM(ID_OPTIONS_DISABLEG3DLOG, g_Config.bDisableG3DLog); CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache); CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.bShowFPSCounter); + CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != 0); UINT enable = !Core_IsStepping() ? MF_GRAYED : MF_ENABLED; EnableMenuItem(menu,ID_EMULATION_RUN, g_State.bEmuThreadStarted ? enable : MF_GRAYED); @@ -874,14 +847,13 @@ namespace MainWindow void SetPlaying(const char *text) { + char temp[256]; if (text == 0) - SetWindowText(hwndMain, "PPSSPP " PPSSPP_VERSION_STR); + snprintf(temp, 256, "PPSSPP %s", PPSSPP_GIT_VERSION); else - { - char temp[256]; - sprintf(temp, "%s - %s", text, "PPSSPP " PPSSPP_VERSION_STR); - SetWindowText(hwndMain,temp); - } + snprintf(temp, 256, "%s - PPSSPP %s", text, PPSSPP_GIT_VERSION); + temp[255] = '\0'; + SetWindowText(hwndMain, temp); } void SaveStateActionFinished(bool result, void *userdata) @@ -890,13 +862,6 @@ namespace MainWindow if (!result) MessageBox(0, "Savestate failure. Please try again later.", "Sorry", MB_OK); SetCursor(LoadCursor(0, IDC_ARROW)); - - if (g_State.bEmuThreadStarted && nextState == CORE_RUNNING) - { - for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_GO, 0); - } } void SetNextState(CoreState state) diff --git a/Windows/git-version-gen.cmd b/Windows/git-version-gen.cmd new file mode 100644 index 000000000000..eaa42ba57dd2 --- /dev/null +++ b/Windows/git-version-gen.cmd @@ -0,0 +1,71 @@ +@echo off +rem // Copyright (c) 2012- PPSSPP Project. + +rem // This program is free software: you can redistribute it and/or modify +rem // it under the terms of the GNU General Public License as published by +rem // the Free Software Foundation, version 2.0 or later versions. + +rem // This program is distributed in the hope that it will be useful, +rem // but WITHOUT ANY WARRANTY; without even the implied warranty of +rem // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +rem // GNU General Public License 2.0 for more details. + +rem // A copy of the GPL 2.0 should have been included with the program. +rem // If not, see http://www.gnu.org/licenses/ + +rem // Official git repository and contact information can be found at +rem // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +setlocal ENABLEDELAYEDEXPANSION + +set GIT_VERSION_FILE=%~p0..\git-version.cpp +if not defined GIT ( + set GIT="git" +) +call %GIT% describe > NUL 2> NUL +if errorlevel 1 ( + echo Git not on path, trying default Msysgit paths + set GIT="%ProgramFiles(x86)%\Git\bin\git.exe" + call !GIT! describe > NUL 2> NUL + if errorlevel 1 ( + set GIT="%ProgramFiles%\Git\bin\git.exe" + ) +) + +if exist "%GIT_VERSION_FILE%" ( + rem // Skip updating the file if PPSSPP_GIT_VERSION_NO_UPDATE is 1. + findstr /B /C:"#define PPSSPP_GIT_VERSION_NO_UPDATE 1" "%GIT_VERSION_FILE%" > NUL + if not errorlevel 1 ( + goto done + ) +) + +call %GIT% describe --always > NUL 2> NUL +if errorlevel 1 ( + echo Unable to update git-version.cpp, git not found. + echo If you don't want to add it to your path, set the GIT environment variable. + + echo // This is a generated file. > "%GIT_VERSION_FILE%" + echo. >> "%GIT_VERSION_FILE%" + echo const char *PPSSPP_GIT_VERSION = "unknown"; >> "%GIT_VERSION_FILE%" + goto done +) + +for /F %%I IN ('call %GIT% describe --always') do set GIT_VERSION=%%I + +rem // Don't modify the file if it already has the current version. +if exist "%GIT_VERSION_FILE%" ( + findstr /C:"%GIT_VERSION%" "%GIT_VERSION_FILE%" > NUL + if not errorlevel 1 ( + goto done + ) +) + +echo // This is a generated file. > "%GIT_VERSION_FILE%" +echo. >> "%GIT_VERSION_FILE%" +echo const char *PPSSPP_GIT_VERSION = "%GIT_VERSION%"; >> "%GIT_VERSION_FILE%" +echo. >> "%GIT_VERSION_FILE%" +echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%" +echo #define PPSSPP_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%" + +:done diff --git a/Windows/main.cpp b/Windows/main.cpp index f729c9abb881..b40541814f52 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -159,8 +159,9 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin EmuThread_Start(fileToStart); } - // else - // MainWindow::BrowseAndBoot(); + if (g_Config.bBrowse) + MainWindow::BrowseAndBoot(); + if (!hideLog) SetForegroundWindow(hwndMain); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index e0a82cd7f423..bcde41f217e1 100644 Binary files a/Windows/ppsspp.rc and b/Windows/ppsspp.rc differ diff --git a/Windows/resource.h b/Windows/resource.h index 91f5936a7ea4..f883f17e5b62 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -260,6 +260,7 @@ #define ID_OPTIONS_VERTEXCACHE 40136 #define ID_OPTIONS_SHOWFPS 40137 #define ID_OPTIONS_STRETCHDISPLAY 40138 +#define ID_OPTIONS_FRAMESKIP 40139 #define IDC_STATIC -1 // Next default values for new objects @@ -267,7 +268,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 233 -#define _APS_NEXT_COMMAND_VALUE 40139 +#define _APS_NEXT_COMMAND_VALUE 40140 #define _APS_NEXT_CONTROL_VALUE 1163 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/android/ab.cmd b/android/ab.cmd index a3ca5c5b51da..36eecad80298 100644 --- a/android/ab.cmd +++ b/android/ab.cmd @@ -1,3 +1,4 @@ +xcopy ..\flash0 assets\flash0 /s /y SET NDK=C:\AndroidNDK SET NDK_MODULE_PATH=..;..\native\ext REM Need to force target-platform to android-9 to get access to OpenSL headers. diff --git a/android/ab.sh b/android/ab.sh index 6190b3e541de..cf79aab3bcf8 100755 --- a/android/ab.sh +++ b/android/ab.sh @@ -1 +1,2 @@ +cp -r ../flash0 assets NDK_MODULE_PATH=..:../native/ext $NDK/ndk-build -j3 TARGET_PLATFORM=android-9 $* diff --git a/android/git-version-gen.sh b/android/git-version-gen.sh new file mode 100755 index 000000000000..d441c38379a3 --- /dev/null +++ b/android/git-version-gen.sh @@ -0,0 +1,52 @@ +#!/bin/bash +## Copyright (c) 2012- PPSSPP Project. + +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, version 2.0 or later versions. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License 2.0 for more details. + +## A copy of the GPL 2.0 should have been included with the program. +## If not, see http://www.gnu.org/licenses/ + +## Official git repository and contact information can be found at +## https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +GIT_VERSION_FILE=$(dirname $0)/../git-version.cpp + +if [ -e "$GIT_VERSION_FILE" ]; then + # Skip updating the file if PPSSPP_GIT_VERSION_NO_UPDATE is 1. + results=$(grep '^#define PPSSPP_GIT_VERSION_NO_UPDATE 1' "$GIT_VERSION_FILE") + if [ "$results" != "" ]; then + exit 0 + fi +fi + +GIT_VERSION=$(git describe --always) +if [ "$GIT_VERSION" == "" ]; then + echo "Unable to update git-version.cpp, git not on path." 1>&2 + + echo "// This is a generated file." > "$GIT_VERSION_FILE" + echo >> "$GIT_VERSION_FILE" + echo 'const char *PPSSPP_GIT_VERSION = "unknown";' >> "$GIT_VERSION_FILE" + exit 0 +fi + +# Don't modify the file if it already has the current version. +if [ -e "$GIT_VERSION_FILE" ]; then + results=$(grep "$GIT_VERSION" "$GIT_VERSION_FILE") + if [ "$results" != "" ]; then + exit 0 + fi +fi + +echo "// This is a generated file." > "$GIT_VERSION_FILE" +echo >> "$GIT_VERSION_FILE" +echo 'const char *PPSSPP_GIT_VERSION = "'"$GIT_VERSION"'";' >> "$GIT_VERSION_FILE" +echo >> "$GIT_VERSION_FILE" +echo "// If you don't want this file to update/recompile, change to 1." >> "$GIT_VERSION_FILE" +echo "#define PPSSPP_GIT_VERSION_NO_UPDATE 0" >> "$GIT_VERSION_FILE" diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f1ea86ef5c0d..2ee383b6d77d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -54,6 +54,7 @@ LOCAL_SRC_FILES := \ $(SRC)/native/android/app-android.cpp \ $(SRC)/ext/disarm.cpp \ $(SRC)/ext/libkirk/AES.c \ + $(SRC)/ext/libkirk/amctrl.c \ $(SRC)/ext/libkirk/SHA1.c \ $(SRC)/ext/libkirk/bn.c \ $(SRC)/ext/libkirk/ec.c \ @@ -104,6 +105,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/PSPLoaders.cpp \ $(SRC)/Core/MemMap.cpp \ $(SRC)/Core/MemMapFunctions.cpp \ + $(SRC)/Core/Reporting.cpp \ $(SRC)/Core/SaveState.cpp \ $(SRC)/Core/System.cpp \ $(SRC)/Core/PSPMixer.cpp \ @@ -115,6 +117,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/Dialog/PSPPlaceholderDialog.cpp \ $(SRC)/Core/Dialog/PSPSaveDialog.cpp \ $(SRC)/Core/Dialog/SavedataParam.cpp \ + $(SRC)/Core/Font/PGF.cpp \ $(SRC)/Core/HLE/HLETables.cpp \ $(SRC)/Core/HLE/HLE.cpp \ $(SRC)/Core/HLE/sceAtrac.cpp \ @@ -157,6 +160,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/HLE/sceUsb.cpp \ $(SRC)/Core/HLE/sceUtility.cpp \ $(SRC)/Core/HLE/sceVaudio.cpp \ + $(SRC)/Core/HLE/scePspNpDrm_user.cpp \ $(SRC)/Core/FileSystems/BlockDevices.cpp \ $(SRC)/Core/FileSystems/ISOFileSystem.cpp \ $(SRC)/Core/FileSystems/MetaFileSystem.cpp \ @@ -184,10 +188,14 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/MIPS/ARM/ArmRegCacheFPU.cpp \ $(SRC)/Core/Util/BlockAllocator.cpp \ $(SRC)/Core/Util/ppge_atlas.cpp \ - $(SRC)/Core/Util/PPGeDraw.cpp + $(SRC)/Core/Util/PPGeDraw.cpp \ + $(SRC)/git-version.cpp include $(BUILD_SHARED_LIBRARY) $(call import-module,libzip) $(call import-module,native) + +jni/$(SRC)/git-version.cpp: + ./git-version-gen.sh diff --git a/android/jni/ArmEmitterTest.cpp b/android/jni/ArmEmitterTest.cpp index b52a68e38a86..66a74ba67196 100644 --- a/android/jni/ArmEmitterTest.cpp +++ b/android/jni/ArmEmitterTest.cpp @@ -20,61 +20,54 @@ TestCode::TestCode() AllocCodeSpace(0x10000); } - -u32 TestLeaf(u32 a, u32 b, u32 c) -{ - ILOG("TestLeaf: %08x %08x %08x\n", a, b, c); - return 0xFF; -} - -void TestLeaf2(u32 a) -{ - ILOG("TestLeaf2 %08x\n"); -} - +static float abc[256] = {1.0f, 2.0f, 0.0f}; void TestCode::Generate() { testCodePtr = this->GetCodePtr(); // Sonic1 commented that R11 is the frame pointer in debug mode, whatever "debug mode" means. PUSH(2, R11, _LR); - MOVI2R(R0, 0x13371338); - AND(R1, R0, Operand2(0xFC, 4)); - BIC(R0, R0, Operand2(0xFC, 4)); - CMP(R1, Operand2(0x10, 4)); - SetCC(CC_EQ); - MOV(R2, Operand2(0x99, 0)); - SetCC(CC_NEQ); - MOV(R2, Operand2(0xFF, 0)); - SetCC(); - QuickCallFunction(R3, (void*)&TestLeaf); + MOVI2R(R11, (u32)&abc[0]); + MOVI2R(R1, 0x3f800000); + STR(R11, R1, 4 * (32 + 31)); + VLDR(S0, R11, 0); + VLDR(S1, R11, 4); + VADD(S12, S0, S1); + VSTR(S0, R11, 4 * (32 + 31)); + VSTR(S12, R11, 4 * (32 + 31)); + //VSTR(S2, R0, 8); + POP(2, R11, _PC); // Yup, this is how you return. + + //VLDR(S1, R0, 4); + //VADD(S2, S0, S1); + //VSTR(S2, R0, 8); + //QuickCallFunction(R3, (void*)&TestLeaf); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x1, 0x100, 0x1337); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x2, 0x100, 0x31337); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x3, 0x100, 0x1337); - POP(2, R11, _PC); // Yup, this is how you return. - - testCodePtr2 = this->GetCodePtr(); - PUSH(2, R11, _LR); - QuickCallFunction(R3, (void*)&TestLeaf2); - POP(2, R11, _PC); } -void CallPtr(const void *ptr) +u32 CallPtr(const void *ptr) { - ((void(*)())ptr)(); + return ((u32(*)())ptr)(); } +extern void DisassembleArm(const u8 *data, int size); void ArmEmitterTest() { + // Disabled for now. + return; ILOG("Running ARM emitter test!"); TestCode gen; - gen.ReserveCodeSpace(0x4000); + gen.ReserveCodeSpace(0x1000); + const u8 *codeStart = gen.GetCodePtr(); gen.Generate(); + DisassembleArm(codeStart, gen.GetCodePtr()-codeStart); - CallPtr(gen.testCodePtr); - ILOG("ARM emitter test 1 passed!"); + u32 retval = CallPtr(gen.testCodePtr); + ILOG("ARM emitter test 1 passed if %f == 3.0! retval = %08x", abc[32 + 31], retval); } diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index b3eb28e4fbb6..8a0d13f84ebd 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -156,7 +156,7 @@ void EmuScreen::render() // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... - int blockTicks = usToCycles(1000000 / 2); + int blockTicks = usToCycles(1000000 / 10); // Run until CORE_NEXTFRAME while (coreState == CORE_RUNNING) { diff --git a/android/jni/GamepadEmu.cpp b/android/jni/GamepadEmu.cpp index 83e0b5342985..826c1900b145 100644 --- a/android/jni/GamepadEmu.cpp +++ b/android/jni/GamepadEmu.cpp @@ -32,7 +32,7 @@ TouchButton buttonLeft(&ui_atlas, I_DIR, I_ARROW, PAD_BUTTON_LEFT, 0); TouchButton buttonUp(&ui_atlas, I_DIR, I_ARROW, PAD_BUTTON_UP, 90); TouchButton buttonRight(&ui_atlas, I_DIR, I_ARROW, PAD_BUTTON_RIGHT, 180); TouchButton buttonDown(&ui_atlas, I_DIR, I_ARROW, PAD_BUTTON_DOWN, 270); -#ifdef __SYMBIAN32__ +#if defined(__SYMBIAN32__) || defined(IOS) TouchButton buttonPause(&ui_atlas, I_RECT, I_ARROW, PAD_BUTTON_BACK, 90); #endif @@ -75,7 +75,7 @@ void LayoutGamepad(int w, int h) buttonLShoulder.setPos(button_spacing + 10 * controlScale, 15 * controlScale, controlScale); buttonRShoulder.setPos(w - button_spacing - 10 * controlScale, 15 * controlScale, controlScale); -#ifdef __SYMBIAN32__ +#if defined(__SYMBIAN32__) || defined(IOS) buttonPause.setPos(halfW, 15 * controlScale, controlScale); #endif @@ -101,7 +101,7 @@ void UpdateGamepad(InputState &input_state) buttonLShoulder.update(input_state); buttonRShoulder.update(input_state); -#ifdef __SYMBIAN32__ +#if defined(__SYMBIAN32__) || defined(IOS) buttonPause.update(input_state); #endif @@ -128,7 +128,7 @@ void DrawGamepad(DrawBuffer &db) buttonLShoulder.draw(db, color, colorOverlay); buttonRShoulder.draw(db, color, colorOverlay); -#ifdef __SYMBIAN32__ +#if defined(__SYMBIAN32__) || defined(IOS) buttonPause.draw(db, color, colorOverlay); #endif diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index 0ac9a4e6eede..a69a5b88ec89 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -24,6 +24,7 @@ #include "base/timeutil.h" #include "base/NativeApp.h" #include "gfx_es2/glsl_program.h" +#include "gfx_es2/gl_state.h" #include "input/input_state.h" #include "math/curves.h" #include "ui/ui.h" @@ -83,6 +84,8 @@ static void DrawBackground(float alpha) { ybase[i] = rng.F() * dp_yres; } } + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(0.1f,0.2f,0.43f,1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); ui_draw2d.DrawImageStretch(I_BG, 0, 0, dp_xres, dp_yres); @@ -165,7 +168,7 @@ void MenuScreen::render() { ui_draw2d.DrawTextShadow(UBUNTU48, "PPSSPP", dp_xres + xoff - w/2, 80, 0xFFFFFFFF, ALIGN_HCENTER | ALIGN_BOTTOM); ui_draw2d.SetFontScale(0.7f, 0.7f); - ui_draw2d.DrawTextShadow(UBUNTU24, PPSSPP_VERSION_STR, dp_xres + xoff, 80, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_BOTTOM); + ui_draw2d.DrawTextShadow(UBUNTU24, PPSSPP_GIT_VERSION, dp_xres + xoff, 80, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_BOTTOM); ui_draw2d.SetFontScale(1.0f, 1.0f); VLinear vlinear(dp_xres + xoff, 95, 20); @@ -193,7 +196,7 @@ void MenuScreen::render() { } if (UIButton(GEN_ID, vlinear, w, "Settings", ALIGN_RIGHT)) { - screenManager()->switchScreen(new SettingsScreen()); + screenManager()->push(new SettingsScreen(), 0); UIReset(); } @@ -251,15 +254,17 @@ void InGameMenuScreen::render() { UICheckBox(GEN_ID, x, y += 50, "Stretch to display", ALIGN_TOPLEFT, &g_Config.bStretchToDisplay); UICheckBox(GEN_ID, x, y += 50, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + bool fs = g_Config.iFrameSkip == 1; + UICheckBox(GEN_ID, x, y += 50, "Frameskip", ALIGN_TOPLEFT, &fs); + g_Config.iFrameSkip = fs ? 1 : 0; // TODO: Add UI for more than one slot. - VLinear vlinear1(x, y + 80, 20); - UIText(UBUNTU24, vlinear1, "Save states are experimental (and large)", 0xFFFFFFFF); - if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Save State", ALIGN_LEFT)) { + HLinear hlinear1(x, y + 80, 20); + if (UIButton(GEN_ID, hlinear1, LARGE_BUTTON_WIDTH, "Save State", ALIGN_LEFT)) { SaveState::SaveSlot(0, 0, 0); screenManager()->finishDialog(this, DR_CANCEL); } - if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Load State", ALIGN_LEFT)) { + if (UIButton(GEN_ID, hlinear1, LARGE_BUTTON_WIDTH, "Load State", ALIGN_LEFT)) { SaveState::LoadSlot(0, 0, 0); screenManager()->finishDialog(this, DR_CANCEL); } @@ -268,13 +273,18 @@ void InGameMenuScreen::render() { if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Continue", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_CANCEL); } + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Settings", ALIGN_RIGHT)) { + screenManager()->push(new SettingsScreen(), 0); + } if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Return to Menu", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_OK); } - + + /* if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres - 10), LARGE_BUTTON_WIDTH*2, "Debug: Dump Next Frame", ALIGN_BOTTOMRIGHT)) { gpu->DumpNextFrame(); } + */ DrawWatermark(); UIEnd(); @@ -286,7 +296,7 @@ void InGameMenuScreen::render() { void SettingsScreen::update(InputState &input) { if (input.pad_buttons_down & PAD_BUTTON_BACK) { g_Config.Save(); - screenManager()->switchScreen(new MenuScreen()); + screenManager()->finishDialog(this, DR_OK); } } @@ -304,10 +314,16 @@ void SettingsScreen::render() { int y = 30; int stride = 40; UICheckBox(GEN_ID, x, y += stride, "Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound); - UICheckBox(GEN_ID, x, y += stride, "Buffered Rendering", ALIGN_TOPLEFT, &g_Config.bBufferedRendering); + if (UICheckBox(GEN_ID, x, y += stride, "Buffered Rendering", ALIGN_TOPLEFT, &g_Config.bBufferedRendering)) { + if (gpu) + gpu->Resized(); + } if (g_Config.bBufferedRendering) { bool doubleRes = g_Config.iWindowZoom == 2; - UICheckBox(GEN_ID, x + 50, y += stride, "2x Render Resolution", ALIGN_TOPLEFT, &doubleRes); + if (UICheckBox(GEN_ID, x + 50, y += stride, "2x Render Resolution", ALIGN_TOPLEFT, &doubleRes)) { + if (gpu) + gpu->Resized(); + } g_Config.iWindowZoom = doubleRes ? 2 : 1; } UICheckBox(GEN_ID, x, y += stride, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); @@ -326,7 +342,7 @@ void SettingsScreen::render() { // UICheckBox(GEN_ID, x, y += stride, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer); if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres-10), LARGE_BUTTON_WIDTH, "Back", ALIGN_RIGHT | ALIGN_BOTTOM)) { - screenManager()->switchScreen(new MenuScreen()); + screenManager()->finishDialog(this, DR_OK); } UIEnd(); @@ -434,9 +450,8 @@ void CreditsScreen::update(InputState &input_state) { frames_++; } -static const char *credits[] = -{ - "PPSSPP " PPSSPP_VERSION_STR, +static const char * credits[] = { + "PPSSPP", "", "", "A fast and portable PSP emulator", @@ -496,6 +511,11 @@ static const char *credits[] = }; void CreditsScreen::render() { + // TODO: This is kinda ugly, done on every frame... + char temp[256]; + snprintf(temp, 256, "PPSSPP %s", PPSSPP_GIT_VERSION); + credits[0] = (const char *)temp; + UIShader_Prepare(); UIBegin(); DrawBackground(1.0f); diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 7bde05e9d266..857c6cbddb68 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -101,11 +101,10 @@ class NativeHost : public Host virtual void InitGL() {} virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() {} virtual void InitSound(PMixer *mixer); - virtual void UpdateSound() {}; + virtual void UpdateSound() {} virtual void ShutdownSound(); // this is sent from EMU thread! Make sure that Host handles it properly! @@ -155,7 +154,9 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo *app_dir_name = "ppsspp"; *landscape = true; - // ArmEmitterTest(); +#if defined(ANDROID) + ArmEmitterTest(); +#endif } void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID) @@ -167,6 +168,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co #ifdef BLACKBERRY // Packed assets are included in app/native/ dir VFSRegister("", new DirectoryAssetReader("app/native/assets/")); +#elif defined(IOS) + VFSRegister("", new DirectoryAssetReader(external_directory)); #else VFSRegister("", new DirectoryAssetReader("assets/")); #endif @@ -180,7 +183,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co LogManager *logman = LogManager::GetInstance(); ILOG("Logman: %p", logman); - config_filename = user_data_path + "ppsspp.ini"; + config_filename = user_data_path + "/ppsspp.ini"; g_Config.Load(config_filename.c_str()); @@ -232,8 +235,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co LogManager::GetInstance()->ChangeFileLog(fileToLog); if (g_Config.currentDirectory == "") { -#if defined(ANDROID) || defined(BLACKBERRY) || defined(__SYMBIAN32__) +#if defined(ANDROID) g_Config.currentDirectory = external_directory; +#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) || defined(IOS) + g_Config.currentDirectory = savegame_directory; #else g_Config.currentDirectory = getenv("HOME"); #endif @@ -245,7 +250,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co // most sense. g_Config.memCardDirectory = std::string(external_directory) + "/"; g_Config.flashDirectory = std::string(external_directory)+"/flash/"; -#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) +#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) || defined(IOS) g_Config.memCardDirectory = user_data_path; g_Config.flashDirectory = user_data_path+"/flash/"; #else @@ -317,6 +322,9 @@ void NativeInitGraphics() void NativeRender() { EnableFZ(); + glstate.depthWrite.set(GL_TRUE); + glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + // Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames. glClearColor(0,0,0,1); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); diff --git a/ext/disarm.cpp b/ext/disarm.cpp index ffd3d756b5f0..a700847367c0 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -308,8 +308,8 @@ instr_disassemble(word instr, address addr, pDisOptions opts) { fpn = ((instr>>15)&1) + ((instr>>21)&2); - result.undefined = - result.badbits = + result.undefined = 0; + result.badbits = 0; result.oddbits = 0; result.is_SWI = 0; result.target_type = target_None; diff --git a/ext/libkirk/AES.c b/ext/libkirk/AES.c index c75748510b2a..c7a84621f2c8 100644 --- a/ext/libkirk/AES.c +++ b/ext/libkirk/AES.c @@ -37,11 +37,11 @@ //CMAC GLOBS #define AES_128 0 -const unsigned char const_Rb[16] = { +static const unsigned char const_Rb[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 }; -const unsigned char const_Zero[16] = { +static const unsigned char const_Zero[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/ext/libkirk/amctrl.c b/ext/libkirk/amctrl.c new file mode 100644 index 000000000000..c499e7cff86e --- /dev/null +++ b/ext/libkirk/amctrl.c @@ -0,0 +1,679 @@ +/* + * amctrl.c -- Reverse engineering of amctrl.prx + * written by tpu. + */ + + +#include +#include +#include + +#include "kirk_engine.h" +#include "AES.h" +#include "SHA1.h" +#include "amctrl.h" + +/*************************************************************/ + +static const u8 loc_1CD4[16] = {0xE3, 0x50, 0xED, 0x1D, 0x91, 0x0A, 0x1F, 0xD0, 0x29, 0xBB, 0x1C, 0x3E, 0xF3, 0x40, 0x77, 0xFB}; +static const u8 loc_1CE4[16] = {0x13, 0x5F, 0xA4, 0x7C, 0xAB, 0x39, 0x5B, 0xA4, 0x76, 0xB8, 0xCC, 0xA9, 0x8F, 0x3A, 0x04, 0x45}; +static const u8 loc_1CF4[16] = {0x67, 0x8D, 0x7F, 0xA3, 0x2A, 0x9C, 0xA0, 0xD1, 0x50, 0x8A, 0xD8, 0x38, 0x5E, 0x4B, 0x01, 0x7E}; + +static u8 kirk_buf[0x0814]; // 1DC0 1DD4 + +/*************************************************************/ + +static int kirk4(u8 *buf, int size, int type) +{ + int retv; + u32 *header = (u32*)buf; + + header[0] = 4; + header[1] = 0; + header[2] = 0; + header[3] = type; + header[4] = size; + + retv = sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size, 4); + + if(retv) + return 0x80510311; + + return 0; +} + +static int kirk7(u8 *buf, int size, int type) +{ + int retv; + u32 *header = (u32*)buf; + + header[0] = 5; + header[1] = 0; + header[2] = 0; + header[3] = type; + header[4] = size; + + retv = sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size, 7); + if(retv) + return 0x80510311; + + return 0; +} + +static int kirk5(u8 *buf, int size) +{ + int retv; + u32 *header = (u32*)buf; + + header[0] = 4; + header[1] = 0; + header[2] = 0; + header[3] = 0x0100; + header[4] = size; + + retv = sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size, 5); + if(retv) + return 0x80510312; + + return 0; +} + +static int kirk8(u8 *buf, int size) +{ + int retv; + u32 *header = (u32*)buf; + + header[0] = 5; + header[1] = 0; + header[2] = 0; + header[3] = 0x0100; + header[4] = size; + + retv = sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size, 8); + if(retv) + return 0x80510312; + + return 0; +} + +static int kirk14(u8 *buf) +{ + int retv; + + retv = sceUtilsBufferCopyWithRange(buf, 0x14, 0, 0, 14); + if(retv) + return 0x80510315; + + return 0; +} + +/*************************************************************/ + +// Called by sceDrmBBMacUpdate +// encrypt_buf +static int sub_158(u8 *buf, int size, u8 *key, int key_type) +{ + int i, retv; + + for(i=0; i<16; i++){ + buf[0x14+i] ^= key[i]; + } + + retv = kirk4(buf, size, key_type); + if(retv) + return retv; + + // copy last 16 bytes to keys + memcpy(key, buf+size+4, 16); + + return 0; +} + + +// type: +// 2: use fuse id +// 3: use fixed id +int sceDrmBBMacInit(MAC_KEY *mkey, int type) +{ + mkey->type = type; + mkey->pad_size = 0; + + memset(mkey->key, 0, 16); + memset(mkey->pad, 0, 16); + + return 0; +} + +int sceDrmBBMacUpdate(MAC_KEY *mkey, u8 *buf, int size) +{ + int retv = 0, ksize, p, type; + u8 *kbuf; + + if(mkey->pad_size>16){ + retv = 0x80510302; + goto _exit; + } + + if(mkey->pad_size+size<=16){ + memcpy(mkey->pad+mkey->pad_size, buf, size); + mkey->pad_size += size; + retv = 0; + }else{ + kbuf = kirk_buf+0x14; + // copy pad data first + memcpy(kbuf, mkey->pad, mkey->pad_size); + + p = mkey->pad_size; + + mkey->pad_size += size; + mkey->pad_size &= 0x0f; + if(mkey->pad_size==0) + mkey->pad_size = 16; + + size -= mkey->pad_size; + // save last data to pad buf + memcpy(mkey->pad, buf+size, mkey->pad_size); + + type = (mkey->type==2)? 0x3A : 0x38; + + while(size){ + ksize = (size+p>=0x0800)? 0x0800 : size+p; + memcpy(kbuf+p, buf, ksize-p); + retv = sub_158(kirk_buf, ksize, mkey->key, type); + if(retv) + goto _exit; + size -= (ksize-p); + buf += ksize-p; + p = 0; + } + } + +_exit: + return retv; + +} + +int sceDrmBBMacFinal(MAC_KEY *mkey, u8 *buf, u8 *vkey) +{ + int i, retv, code; + u8 *kbuf, tmp[16], tmp1[16]; + u32 t0, v0, v1; + + if(mkey->pad_size>16) + return 0x80510302; + + code = (mkey->type==2)? 0x3A : 0x38; + kbuf = kirk_buf+0x14; + + memset(kbuf, 0, 16); + retv = kirk4(kirk_buf, 16, code); + if(retv) + goto _exit; + memcpy(tmp, kbuf, 16); + + // left shift tmp 1 bit + t0 = (tmp[0]&0x80)? 0x87 : 0; + for(i=0; i<15; i++){ + v1 = tmp[i+0]; + v0 = tmp[i+1]; + v1 <<= 1; + v0 >>= 7; + v0 |= v1; + tmp[i+0] = v0; + } + v0 = tmp[15]; + v0 <<= 1; + v0 ^= t0; + tmp[15] = v0; + + // padding remain data + if(mkey->pad_size<16){ + // left shift tmp 1 bit + t0 = (tmp[0]&0x80)? 0x87 : 0; + for(i=0; i<15; i++){ + v1 = tmp[i+0]; + v0 = tmp[i+1]; + v1 <<= 1; + v0 >>= 7; + v0 |= v1; + tmp[i+0] = v0; + } + v0 = tmp[15]; + v0 <<= 1; + v0 ^= t0; + tmp[15] = v0; + + mkey->pad[mkey->pad_size] = 0x80; + if(mkey->pad_size+1<16) + memset(mkey->pad+mkey->pad_size+1, 0, 16-mkey->pad_size-1); + } + + for(i=0; i<16; i++){ + mkey->pad[i] ^= tmp[i]; + } + + memcpy(kbuf, mkey->pad, 16); + memcpy(tmp1, mkey->key, 16); + + retv = sub_158(kirk_buf, 0x10, tmp1, code); + if(retv) + return retv; + + for(i=0; i<0x10; i++){ + tmp1[i] ^= loc_1CD4[i]; + } + + if(mkey->type==2){ + memcpy(kbuf, tmp1, 16); + + retv = kirk5(kirk_buf, 0x10); + if(retv) + goto _exit; + + retv = kirk4(kirk_buf, 0x10, code); + if(retv) + goto _exit; + + memcpy(tmp1, kbuf, 16); + } + + if(vkey){ + for(i=0; i<0x10; i++){ + tmp1[i] ^= vkey[i]; + } + memcpy(kbuf, tmp1, 16); + + retv = kirk4(kirk_buf, 0x10, code); + if(retv) + goto _exit; + + memcpy(tmp1, kbuf, 16); + } + + memcpy(buf, tmp1, 16); + + memset(mkey->key, 0, 16); + memset(mkey->pad, 0, 16); + + mkey->pad_size = 0; + mkey->type = 0; + retv = 0; + +_exit: + return retv; +} + +int sceDrmBBMacFinal2(MAC_KEY *mkey, u8 *out, u8 *vkey) +{ + int i, retv, type; + u8 *kbuf, tmp[16]; + + type = mkey->type; + retv = sceDrmBBMacFinal(mkey, tmp, vkey); + if(retv) + return retv; + + kbuf = kirk_buf+0x14; + + // decrypt bbmac + if(type==3){ + memcpy(kbuf, out, 0x10); + kirk7(kirk_buf, 0x10, 0x63); + }else{ + memcpy(kirk_buf, out, 0x10); + } + + retv = 0; + for(i=0; i<0x10; i++){ + if(kirk_buf[i]!=tmp[i]){ + retv = 0x80510300; + break; + } + } + + return retv; +} + +/*************************************************************/ + +static int sub_1F8(u8 *buf, int size, u8 *key, int key_type) +{ + int i, retv; + u8 tmp[16]; + + // copy last 16 bytes to tmp + memcpy(tmp, buf+size+0x14-16, 16); + + retv = kirk7(buf, size, key_type); + if(retv) + return retv; + + for(i=0; i<16; i++){ + buf[i] ^= key[i]; + } + + // copy last 16 bytes to keys + memcpy(key, tmp, 16); + + return 0; +} + + +static int sub_428(u8 *kbuf, u8 *dbuf, int size, CIPHER_KEY *ckey) +{ + int i, retv; + u8 tmp1[16], tmp2[16]; + + memcpy(kbuf+0x14, ckey->key, 16); + + for(i=0; i<16; i++){ + kbuf[0x14+i] ^= loc_1CF4[i]; + } + + if(ckey->type==2) + retv = kirk8(kbuf, 16); + else + retv = kirk7(kbuf, 16, 0x39); + if(retv) + return retv; + + for(i=0; i<16; i++){ + kbuf[i] ^= loc_1CE4[i]; + } + + memcpy(tmp2, kbuf, 0x10); + + if(ckey->seed==1){ + memset(tmp1, 0, 0x10); + }else{ + memcpy(tmp1, tmp2, 0x10); + *(u32*)(tmp1+0x0c) = ckey->seed-1; + } + + for(i=0; iseed; + ckey->seed += 1; + } + + retv = sub_1F8(kbuf, size, tmp1, 0x63); + if(retv) + return retv; + + for(i=0; itype = type; + if(mode==2){ + ckey->seed = seed+1; + for(i=0; i<16; i++){ + ckey->key[i] = header_key[i]; + } + if(version_key){ + for(i=0; i<16; i++){ + ckey->key[i] ^= version_key[i]; + } + } + retv = 0; + }else if(mode==1){ + ckey->seed = 1; + retv = kirk14(kirk_buf); + if(retv) + return retv; + + memcpy(kbuf, kirk_buf, 0x10); + memset(kbuf+0x0c, 0, 4); + + if(ckey->type==2){ + for(i=0; i<16; i++){ + kbuf[i] ^= loc_1CE4[i]; + } + retv = kirk5(kirk_buf, 0x10); + for(i=0; i<16; i++){ + kbuf[i] ^= loc_1CF4[i]; + } + }else{ + for(i=0; i<16; i++){ + kbuf[i] ^= loc_1CE4[i]; + } + retv = kirk4(kirk_buf, 0x10, 0x39); + for(i=0; i<16; i++){ + kbuf[i] ^= loc_1CF4[i]; + } + } + if(retv) + return retv; + + memcpy(ckey->key, kbuf, 0x10); + memcpy(header_key, kbuf, 0x10); + + if(version_key){ + for(i=0; i<16; i++){ + ckey->key[i] ^= version_key[i]; + } + } + }else{ + retv = 0; + } + + return retv; +} + +int sceDrmBBCipherUpdate(CIPHER_KEY *ckey, u8 *data, int size) +{ + int p, retv, dsize; + + retv = 0; + p = 0; + + while(size>0){ + dsize = (size>=0x0800)? 0x0800 : size; + retv = sub_428(kirk_buf, data+p, dsize, ckey); + if(retv) + break; + size -= dsize; + p += dsize; + } + + return retv; +} + +int sceDrmBBCipherFinal(CIPHER_KEY *ckey) +{ + memset(ckey->key, 0, 16); + ckey->type = 0; + ckey->seed = 0; + + return 0; +} + +/*************************************************************/ + +// AES128 encrypt key +static const u8 key_357C[0x30] = { + 0x07,0x3D,0x9E,0x9D,0xA8,0xFD,0x3B,0x2F,0x63,0x18,0x93,0x2E,0xF8,0x57,0xA6,0x64, + 0x37,0x49,0xB7,0x01,0xCA,0xE2,0xE0,0xC5,0x44,0x2E,0x06,0xB6,0x1E,0xFF,0x84,0xF2, + 0x9D,0x31,0xB8,0x5A,0xC8,0xFA,0x16,0x80,0x73,0x60,0x18,0x82,0x18,0x77,0x91,0x9D, +}; + +static const u8 key_363C[16] = { + 0x38,0x20,0xD0,0x11,0x07,0xA3,0xFF,0x3E,0x0A,0x4C,0x20,0x85,0x39,0x10,0xB5,0x54, +}; + +int sceNpDrmGetFixedKey(u8 *key, char *npstr, int type) +{ + AES_ctx akey; + MAC_KEY mkey; + char strbuf[0x30]; + int retv; + + if((type&0x01000000)==0) + return 0x80550901; + type &= 0x000000ff; + + memset(strbuf, 0, 0x30); + strncpy(strbuf, npstr, 0x30); + + retv = sceDrmBBMacInit(&mkey, 1); + if(retv) + return retv; + + retv = sceDrmBBMacUpdate(&mkey, (u8*)strbuf, 0x30); + if(retv) + return retv; + + retv = sceDrmBBMacFinal(&mkey, key, (u8*)key_363C); + if(retv) + return 0x80550902; + + if(type==0) + return 0; + if(type>3) + return 0x80550901; + type = (type-1)*16; + + AES_set_key(&akey, &key_357C[type], 128); + AES_encrypt(&akey, key, key); + + return 0; +} + + +/*************************************************************/ + + +static const u8 dnas_key1A90[] = {0xED,0xE2,0x5D,0x2D,0xBB,0xF8,0x12,0xE5,0x3C,0x5C,0x59,0x32,0xFA,0xE3,0xE2,0x43}; +static const u8 dnas_key1AA0[] = {0x27,0x74,0xFB,0xEB,0xA4,0xA0, 1,0xD7, 2,0x56,0x9E,0x33,0x8C,0x19,0x57,0x83}; + +PGD_DESC *pgd_open(u8 *pgd_buf, int pgd_flag, u8 *pgd_vkey) +{ + PGD_DESC *pgd; + MAC_KEY mkey; + CIPHER_KEY ckey; + u8 *fkey; + int retv; + + //DEBUG_LOG(HLE, "Open PGD ..."); + + pgd = (PGD_DESC*)malloc(sizeof(PGD_DESC)); + memset(pgd, 0, sizeof(PGD_DESC)); + + pgd->key_index = *(u32*)(pgd_buf+4); + pgd->drm_type = *(u32*)(pgd_buf+8); + + if(pgd->drm_type==1){ + pgd->mac_type = 1; + pgd_flag |= 4; + if(pgd->key_index>1){ + pgd->mac_type = 3; + pgd_flag |= 8; + } + pgd->cipher_type = 1; + }else{ + pgd->mac_type = 2; + pgd->cipher_type = 2; + } + pgd->open_flag = pgd_flag; + + // select fixed key + fkey = NULL; + if(pgd_flag&2) + fkey = (u8*)dnas_key1A90; + if(pgd_flag&1) + fkey = (u8*)dnas_key1AA0; + if(fkey==NULL){ + //ERROR_LOG(HLE, "pgd_open: invalid pgd_flag! %08x\n", pgd_flag); + free(pgd); + return NULL; + } + + // MAC_0x80 check + sceDrmBBMacInit(&mkey, pgd->mac_type); + sceDrmBBMacUpdate(&mkey, pgd_buf+0x00, 0x80); + retv = sceDrmBBMacFinal2(&mkey, pgd_buf+0x80, fkey); + if(retv){ + //ERROR_LOG(HLE, "pgd_open: MAC_80 check failed!: %08x(%d)\n", retv, retv); + free(pgd); + return NULL; + } + + // MAC_0x70 + sceDrmBBMacInit(&mkey, pgd->mac_type); + sceDrmBBMacUpdate(&mkey, pgd_buf+0x00, 0x70); + if(pgd_vkey){ + // use given vkey + retv = sceDrmBBMacFinal2(&mkey, pgd_buf+0x70, pgd_vkey); + if(retv){ + //ERROR_LOG(HLE, "pgd_open: MAC_70 check failed!: %08x(%d)\n", retv, retv); + free(pgd); + return NULL; + }else{ + memcpy(pgd->vkey, pgd_vkey, 16); + } + }else{ + //ERROR_LOG(HLE, "pgd_open: need key!\n"); + free(pgd); + return NULL; + } + + + // decrypt PGD_DESC + sceDrmBBCipherInit(&ckey, pgd->cipher_type, 2, pgd_buf+0x10, pgd->vkey, 0); + sceDrmBBCipherUpdate(&ckey, pgd_buf+0x30, 0x30); + sceDrmBBCipherFinal(&ckey); + + pgd->data_size = *(u32*)(pgd_buf+0x44); + pgd->block_size = *(u32*)(pgd_buf+0x48); + pgd->data_offset = *(u32*)(pgd_buf+0x4c); + memcpy(pgd->dkey, pgd_buf+0x30, 16); + + pgd->align_size = (pgd->data_size+15)&~15; + pgd->table_offset = pgd->data_offset+pgd->align_size; + pgd->block_nr = (pgd->align_size+pgd->block_size-1)&~(pgd->block_size-1); + pgd->block_nr = pgd->block_nr/pgd->block_size; + + pgd->file_offset = 0; + pgd->current_block = -1; + pgd->block_buf = (u8*)malloc(pgd->block_size*2); + + return pgd; +} + +int pgd_decrypt_block(PGD_DESC *pgd, int block) +{ + CIPHER_KEY ckey; + u32 block_offset; + + block_offset = block*pgd->block_size; + + // decrypt block data + sceDrmBBCipherInit(&ckey, pgd->cipher_type, 2, pgd->dkey, pgd->vkey, block_offset>>4); + sceDrmBBCipherUpdate(&ckey, pgd->block_buf, pgd->block_size); + sceDrmBBCipherFinal(&ckey); + + return pgd->block_size; +} + +int pgd_close(PGD_DESC *pgd) +{ + free(pgd->block_buf); + free(pgd); + return 0; +} + +/*************************************************************/ + diff --git a/ext/libkirk/amctrl.h b/ext/libkirk/amctrl.h new file mode 100644 index 000000000000..9a22ac956730 --- /dev/null +++ b/ext/libkirk/amctrl.h @@ -0,0 +1,65 @@ +#ifndef AMCTRL_H +#define AMCTRL_H + +typedef struct { + int type; + u8 key[16]; + u8 pad[16]; + int pad_size; +} MAC_KEY; + +typedef struct +{ + u32 type; + u32 seed; + u8 key[16]; +} CIPHER_KEY; + +typedef struct { + u8 vkey[16]; // key to decrypt PGD header + u8 dkey[16]; // key to decrypt PGD data + + u32 open_flag; + u32 key_index; + u32 drm_type; + u32 mac_type; + u32 cipher_type; + + u32 data_size; + u32 align_size; + u32 block_size; + u32 block_nr; + u32 data_offset; + u32 table_offset; + + u8 *block_buf; + u32 current_block; + u32 file_offset; +}PGD_DESC; + + +// type: +// 2: use fuse id +// 3: use fixed key. MAC need encrypt again +int sceDrmBBMacInit(MAC_KEY *mkey, int type); +int sceDrmBBMacUpdate(MAC_KEY *mkey, u8 *buf, int size); +int sceDrmBBMacFinal(MAC_KEY *mkey, u8 *buf, u8 *vkey); +int sceDrmBBMacFinal2(MAC_KEY *mkey, u8 *out, u8 *vkey); + +// type: 1 use fixed key +// 2 use fuse id +// mode: 1 for encrypt +// 2 for decrypt +int sceDrmBBCipherInit(CIPHER_KEY *ckey, int type, int mode, u8 *header_key, u8 *version_key, u32 seed); +int sceDrmBBCipherUpdate(CIPHER_KEY *ckey, u8 *data, int size); +int sceDrmBBCipherFinal(CIPHER_KEY *ckey); + +// npdrm.prx +int sceNpDrmGetFixedKey(u8 *key, char *npstr, int type); + +PGD_DESC *pgd_open(u8 *pgd_buf, int pgd_flag, u8 *pgd_vkey); +int pgd_decrypt_block(PGD_DESC *pgd, int block); +int pgd_close(PGD_DESC *pgd); + +#endif + diff --git a/ext/libkirk/kirk_engine.h b/ext/libkirk/kirk_engine.h index e89fa54689bd..7edbe4a8dfe3 100644 --- a/ext/libkirk/kirk_engine.h +++ b/ext/libkirk/kirk_engine.h @@ -184,7 +184,7 @@ typedef struct 0x0C: Mul1 0x0D: Mul2 0x0E: Random Number Gen - 0x0F: (absolutely no idea – could be KIRK initialization) + 0x0F: (absolutely no idea ?could be KIRK initialization) 0x10: Signature Gen // Sig Checks 0x11: Signature Check (checks for generated sigs) diff --git a/ext/libkirk/libkirk.vcxproj b/ext/libkirk/libkirk.vcxproj index 3c248e88ea99..20cba7c8c29e 100644 --- a/ext/libkirk/libkirk.vcxproj +++ b/ext/libkirk/libkirk.vcxproj @@ -71,6 +71,7 @@ Level3 Disabled Default + _MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS true @@ -116,6 +117,7 @@ + @@ -123,6 +125,7 @@ + diff --git a/ext/libkirk/libkirk.vcxproj.filters b/ext/libkirk/libkirk.vcxproj.filters index b43b401d6dae..cea5d1c8cdf3 100644 --- a/ext/libkirk/libkirk.vcxproj.filters +++ b/ext/libkirk/libkirk.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -41,6 +44,9 @@ Header Files + + Header Files + diff --git a/flash0/font/jpn0.pgf b/flash0/font/jpn0.pgf new file mode 100644 index 000000000000..913bd73b114c Binary files /dev/null and b/flash0/font/jpn0.pgf differ diff --git a/flash0/font/kr0.pgf b/flash0/font/kr0.pgf new file mode 100644 index 000000000000..5d4f615b9cff Binary files /dev/null and b/flash0/font/kr0.pgf differ diff --git a/flash0/font/ltn0.pgf b/flash0/font/ltn0.pgf new file mode 100644 index 000000000000..bf710feef03f Binary files /dev/null and b/flash0/font/ltn0.pgf differ diff --git a/flash0/font/ltn1.pgf b/flash0/font/ltn1.pgf new file mode 100644 index 000000000000..7b9dede48461 Binary files /dev/null and b/flash0/font/ltn1.pgf differ diff --git a/flash0/font/ltn10.pgf b/flash0/font/ltn10.pgf new file mode 100644 index 000000000000..a12d1814ca30 Binary files /dev/null and b/flash0/font/ltn10.pgf differ diff --git a/flash0/font/ltn11.pgf b/flash0/font/ltn11.pgf new file mode 100644 index 000000000000..402bf6084c93 Binary files /dev/null and b/flash0/font/ltn11.pgf differ diff --git a/flash0/font/ltn12.pgf b/flash0/font/ltn12.pgf new file mode 100644 index 000000000000..092d5ef0418f Binary files /dev/null and b/flash0/font/ltn12.pgf differ diff --git a/flash0/font/ltn13.pgf b/flash0/font/ltn13.pgf new file mode 100644 index 000000000000..89b28d366a8d Binary files /dev/null and b/flash0/font/ltn13.pgf differ diff --git a/flash0/font/ltn14.pgf b/flash0/font/ltn14.pgf new file mode 100644 index 000000000000..873afb3d05f3 Binary files /dev/null and b/flash0/font/ltn14.pgf differ diff --git a/flash0/font/ltn15.pgf b/flash0/font/ltn15.pgf new file mode 100644 index 000000000000..c517fe05cd0d Binary files /dev/null and b/flash0/font/ltn15.pgf differ diff --git a/flash0/font/ltn2.pgf b/flash0/font/ltn2.pgf new file mode 100644 index 000000000000..9ee2763f85fd Binary files /dev/null and b/flash0/font/ltn2.pgf differ diff --git a/flash0/font/ltn3.pgf b/flash0/font/ltn3.pgf new file mode 100644 index 000000000000..805c2863304b Binary files /dev/null and b/flash0/font/ltn3.pgf differ diff --git a/flash0/font/ltn4.pgf b/flash0/font/ltn4.pgf new file mode 100644 index 000000000000..12b075101d98 Binary files /dev/null and b/flash0/font/ltn4.pgf differ diff --git a/flash0/font/ltn5.pgf b/flash0/font/ltn5.pgf new file mode 100644 index 000000000000..d12e9eccc387 Binary files /dev/null and b/flash0/font/ltn5.pgf differ diff --git a/flash0/font/ltn6.pgf b/flash0/font/ltn6.pgf new file mode 100644 index 000000000000..0f626ddff23f Binary files /dev/null and b/flash0/font/ltn6.pgf differ diff --git a/flash0/font/ltn7.pgf b/flash0/font/ltn7.pgf new file mode 100644 index 000000000000..ba9afda58c87 Binary files /dev/null and b/flash0/font/ltn7.pgf differ diff --git a/flash0/font/ltn8.pgf b/flash0/font/ltn8.pgf new file mode 100644 index 000000000000..a07a1f718e8d Binary files /dev/null and b/flash0/font/ltn8.pgf differ diff --git a/flash0/font/ltn9.pgf b/flash0/font/ltn9.pgf new file mode 100644 index 000000000000..11132d1a32e9 Binary files /dev/null and b/flash0/font/ltn9.pgf differ diff --git a/git-version.cmake b/git-version.cmake new file mode 100644 index 000000000000..f26cf9554276 --- /dev/null +++ b/git-version.cmake @@ -0,0 +1,43 @@ +set(GIT_VERSION_FILE "${SOURCE_DIR}/git-version.cpp") +set(GIT_VERSION "unknown") +set(GIT_VERSION_UPDATE "1") + +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --always + WORKING_DIRECTORY ${SOURCE_DIR} + RESULT_VARIABLE exit_code + OUTPUT_VARIABLE GIT_VERSION) + if(NOT ${exit_code} EQUAL 0) + message(WARNING "git describe failed, unable to include version.") + endif() + string(STRIP ${GIT_VERSION} GIT_VERSION) +else() + message(WARNING "git not found, unable to include version.") +endif() + +if(EXISTS ${GIT_VERSION_FILE}) + # Don't update if marked not to update. + file(STRINGS ${GIT_VERSION_FILE} match + REGEX "PPSSPP_GIT_VERSION_NO_UPDATE = 1") + if(NOT $[match} EQUAL "") + set(GIT_VERSION_UPDATE "0") + endif() + + # Don't update if it's already the same. + file(STRINGS ${GIT_VERSION_FILE} match + REGEX "${GIT_VERSION}") + if(NOT $[match} EQUAL "") + set(GIT_VERSION_UPDATE "0") + endif() +endif() + +set(code_string "// This is a generated file.\n\n" + "const char *PPSSPP_GIT_VERSION = \"${GIT_VERSION}\"\;\n\n" + "// If you don't want this file to update/recompile, change to 1.\n" + "#define PPSSPP_GIT_VERSION_NO_UPDATE 0\n") + +message(WARNING "UPDATE: ${GIT_VERSION_FILE}") +if ("${GIT_VERSION_UPDATE}" EQUAL "1") + file(WRITE ${GIT_VERSION_FILE} ${code_string}) +endif() \ No newline at end of file diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 7129523f43fb..f8db1ff0fbbb 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -4,18 +4,19 @@ #include -#include "../Core/Config.h" -#include "../Core/Core.h" -#include "../Core/CoreTiming.h" -#include "../Core/System.h" -#include "../Core/MIPS/MIPS.h" -#include "../Core/Host.h" +#include "Core/Config.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/System.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Host.h" #include "Log.h" #include "LogManager.h" #include "Compare.h" #include "StubHost.h" #ifdef _WIN32 +#include "Windows/OpenGLBase.h" #include "WindowsHeadlessHost.h" #endif @@ -46,6 +47,9 @@ class PrintfLogger : public LogListener } }; +// Temporary hack around annoying linking error. +void GL_SwapBuffers() { } + void printUsage(const char *progname, const char *reason) { if (reason != NULL) @@ -74,7 +78,7 @@ void printUsage(const char *progname, const char *reason) int main(int argc, const char* argv[]) { bool fullLog = false; - bool useJit = false; + bool useJit = true; bool autoCompare = false; bool useGraphics = false; @@ -192,8 +196,10 @@ int main(int argc, const char* argv[]) mipsr4k.RunLoopUntil(nowTicks + frameTicks); // If we were rendering, this might be a nice time to do something about it. - if (coreState == CORE_NEXTFRAME) + if (coreState == CORE_NEXTFRAME) { coreState = CORE_RUNNING; + headlessHost->SwapBuffers(); + } } host->ShutdownGL(); diff --git a/headless/StubHost.h b/headless/StubHost.h index 1533c381c30f..19e71c3b68cb 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -35,7 +35,6 @@ class HeadlessHost : public Host virtual void InitGL() {} virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() {} virtual void InitSound(PMixer *mixer) {} @@ -53,4 +52,9 @@ class HeadlessHost : public Host virtual void SetComparisonScreenshot(const std::string &filename) {} virtual bool isGLWorking() { return false; } + + + // Unique for HeadlessHost + virtual void SwapBuffers() {} + }; \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 5e7448ae2371..a6629982f2db 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -218,7 +218,7 @@ void WindowsHeadlessHost::BeginFrame() } -void WindowsHeadlessHost::EndFrame() +void WindowsHeadlessHost::SwapBuffers() { - SwapBuffers(hDC); + ::SwapBuffers(hDC); } diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 5abc20ca0545..d624f4d3ccea 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -30,10 +30,11 @@ class WindowsHeadlessHost : public HeadlessHost public: virtual void InitGL(); virtual void BeginFrame(); - virtual void EndFrame(); virtual void ShutdownGL(); virtual bool isGLWorking() { return glOkay; } + virtual void SwapBuffers(); + virtual void SendDebugOutput(const std::string &output); virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h); virtual void SetComparisonScreenshot(const std::string &filename); diff --git a/ios/PPSSPP-Info.plist b/ios/PPSSPP-Info.plist index 5fe1a739c6dd..0f42d9ca8a07 100644 --- a/ios/PPSSPP-Info.plist +++ b/ios/PPSSPP-Info.plist @@ -5,15 +5,15 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - ${PRODUCT_NAME} + PPSSPP CFBundleExecutable - ${EXECUTABLE_NAME} + PPSSPP CFBundleIdentifier - com.rock88dev.${PRODUCT_NAME:rfc1034identifier} + com.rock88dev.PPSSPP CFBundleInfoDictionaryVersion 6.0 CFBundleName - ${PRODUCT_NAME} + PPSSPP CFBundlePackageType APPL CFBundleShortVersionString @@ -37,10 +37,15 @@ UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + CFBundleIconFiles + + assets/Icon-72.png + assets/Icon-72@2x.png + assets/Icon.png + assets/Icon@2x.png + diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 576d3b68cac2..4b53a3e612e2 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -13,10 +13,9 @@ #include "file/zip_read.h" #include "input/input_state.h" #include "net/resolve.h" -#include "ui_atlas.h" #include "ui/screen.h" -#include "Config.h" +#include "Core/Config.h" #include "gfx_es2/fbo.h" #define IS_IPAD() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) @@ -34,6 +33,8 @@ extern std::string ram_temp_file; +ViewController* sharedViewController; + @interface ViewController () @property (strong, nonatomic) EAGLContext *context; @@ -50,17 +51,18 @@ - (id)init { self = [super init]; if (self) { + sharedViewController = self; self.touches = [[[NSMutableArray alloc] init] autorelease]; self.documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; - self.bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/"]; + self.bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; memset(&input_state, 0, sizeof(input_state)); net::Init(); ram_temp_file = [[NSTemporaryDirectory() stringByAppendingPathComponent:@"ram_tmp.file"] fileSystemRepresentation]; - NativeInit(0, NULL, [self.bundlePath UTF8String], [self.documentsPath UTF8String], NULL); + NativeInit(0, NULL, [self.documentsPath UTF8String], [self.bundlePath UTF8String], NULL); } return self; @@ -230,11 +232,21 @@ - (void)touchesEnded:(NSSet *)_touches withEvent:(UIEvent *)event } } +- (void)bindDefaultFBO +{ + [(GLKView*)self.view bindDrawable]; +} + void LaunchBrowser(char const* url) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithCString:url encoding:NSStringEncodingConversionAllowLossy]]]; } +void bindDefaultFBO() +{ + [sharedViewController bindDefaultFBO]; +} + void EnableFZ(){}; void DisableFZ(){}; diff --git a/ios/assets/Icon-72.png b/ios/assets/Icon-72.png new file mode 100644 index 000000000000..1ae6c4655737 Binary files /dev/null and b/ios/assets/Icon-72.png differ diff --git a/ios/assets/Icon-72@2x.png b/ios/assets/Icon-72@2x.png new file mode 100644 index 000000000000..3fe685c43e10 Binary files /dev/null and b/ios/assets/Icon-72@2x.png differ diff --git a/ios/assets/Icon.png b/ios/assets/Icon.png new file mode 100644 index 000000000000..5852d4fb0a62 Binary files /dev/null and b/ios/assets/Icon.png differ diff --git a/ios/assets/Icon@2x.png b/ios/assets/Icon@2x.png new file mode 100644 index 000000000000..fa0829e92648 Binary files /dev/null and b/ios/assets/Icon@2x.png differ diff --git a/ios/ios.toolchain.cmake b/ios/ios.toolchain.cmake index 9af6cb06e6de..7c0e2a778f97 100644 --- a/ios/ios.toolchain.cmake +++ b/ios/ios.toolchain.cmake @@ -31,9 +31,12 @@ # Standard settings set (CMAKE_SYSTEM_NAME Darwin) set (CMAKE_SYSTEM_VERSION 1) +set (CMAKE_SYSTEM_PROCESSOR arm) set (UNIX True) set (APPLE True) set (IOS True) +set (APP_TYPE MACOSX_BUNDLE) +set (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 5.0) # Determine the cmake host system version so we know where to find the iOS SDKs find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) @@ -42,12 +45,13 @@ if (CMAKE_UNAME) string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") endif (CMAKE_UNAME) -# Force the compilers to gcc for iOS +# Force the compilers to clang for iOS include (CMakeForceCompiler) CMAKE_FORCE_C_COMPILER (gcc gcc) CMAKE_FORCE_CXX_COMPILER (g++ g++) # Skip the platform compiler checks for cross compiling +set (CMAKE_CROSSCOMPILING TRUE) set (CMAKE_CXX_COMPILER_WORKS TRUE) set (CMAKE_C_COMPILER_WORKS TRUE) @@ -105,12 +109,9 @@ endif (${IOS_PLATFORM} STREQUAL "OS") # Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT # Note Xcode 4.3 changed the installation location, choose the most recent one available set (XCODE_POST_43_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) if (EXISTS ${XCODE_POST_43_ROOT}) set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT}) - elseif(EXISTS ${XCODE_PRE_43_ROOT}) - set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT}) endif (EXISTS ${XCODE_POST_43_ROOT}) endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") diff --git a/native b/native index f5e7bd5a9c3e..dff0ac566954 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit f5e7bd5a9c3e4b39a4eac71b577f06ad54f2bc96 +Subproject commit dff0ac566954bfa18f0a972839a03bea737b821b diff --git a/pspautotests b/pspautotests index c4427bd55d57..a9c949b87dc6 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit c4427bd55d57af2484f5ccd4ec1bed2bcf674395 +Subproject commit a9c949b87dc69d4d6cde854b73455a170a2aaff4 diff --git a/test.py b/test.py index 2d98e03079bc..d2836664b587 100755 --- a/test.py +++ b/test.py @@ -60,13 +60,14 @@ def target(): "misc/testgp", "misc/libc", "misc/dcache", + "mstick/mstick", "string/string", "gpu/callbacks/ge_callbacks", - "gpu/displaylist/state", "threads/alarm/alarm", "threads/alarm/cancel/cancel", "threads/alarm/refer/refer", "threads/alarm/set/set", + "threads/callbacks/callbacks", "threads/events/events", "threads/events/cancel/cancel", "threads/events/clear/clear", @@ -77,13 +78,14 @@ def target(): "threads/events/set/set", "threads/events/wait/wait", "threads/k0/k0", - "threads/lwmutex/create/create", - "threads/lwmutex/delete/delete", - "threads/lwmutex/lock/lock", - "threads/lwmutex/priority/priority", - "threads/lwmutex/try/try", - "threads/lwmutex/try600/try600", - "threads/lwmutex/unlock/unlock", + "threads/lwmutex/create", + "threads/lwmutex/delete", + "threads/lwmutex/lock", + "threads/lwmutex/priority", + "threads/lwmutex/refer", + "threads/lwmutex/try", + "threads/lwmutex/try600", + "threads/lwmutex/unlock", "threads/mbx/mbx", "threads/mbx/cancel/cancel", "threads/mbx/create/create", @@ -93,13 +95,14 @@ def target(): "threads/mbx/receive/receive", "threads/mbx/refer/refer", "threads/mbx/send/send", + "threads/mutex/create", + "threads/mutex/delete", + "threads/mutex/lock", "threads/mutex/mutex", - "threads/mutex/create/create", - "threads/mutex/delete/delete", - "threads/mutex/lock/lock", - "threads/mutex/priority/priority", - "threads/mutex/try/try", - "threads/mutex/unlock/unlock", + "threads/mutex/priority", + "threads/mutex/refer", + "threads/mutex/try", + "threads/mutex/unlock", "threads/semaphores/semaphores", "threads/semaphores/cancel/cancel", "threads/semaphores/create/create", @@ -109,12 +112,17 @@ def target(): "threads/semaphores/refer/refer", "threads/semaphores/signal/signal", "threads/semaphores/wait/wait", - "threads/vpl/vpl", + "threads/threads/release", + "threads/threads/rotate", + "threads/threads/threadend", + "threads/threads/threads", + "threads/vpl/cancel", "threads/vpl/delete", "threads/vpl/free", "threads/vpl/priority", "threads/vpl/refer", "threads/vpl/try", + "threads/vpl/vpl", "power/power", "umd/callbacks/umd", "umd/wait/wait", @@ -122,42 +130,49 @@ def target(): ] tests_next = [ +# These are the next tests up for fixing. These run by default. + "audio/atrac/atractest", + "audio/mp3/mp3test", "audio/sascore/sascore", + "display/vblankmulti", "malloc/malloc", -# These are the next tests up for fixing. These run by default. "threads/fpl/fpl", "threads/k0/k0", "threads/msgpipe/msgpipe", "threads/scheduling/scheduling", - "threads/threads/threads", + "threads/threads/create", "threads/vtimers/vtimer", "threads/vpl/allocate", "threads/vpl/create", "threads/wakeup/wakeup", + "gpu/commands/basic", + "gpu/complex/complex", + "gpu/displaylist/state", + "gpu/reflection/reflection", + "gpu/rendertarget/rendertarget", + "gpu/signals/jumps", + "gpu/signals/simple", "gpu/simple/simple", "gpu/triangle/triangle", - "gpu/commands/basic", "hle/check_not_used_uids", "font/fonttest", "io/cwd/cwd", + "io/file/file", "io/io/io", "io/iodrv/iodrv", "modules/loadexec/loader", "rtc/rtc", + "sysmem/partition", + "sysmem/sysmem", "umd/io/umd_io", "umd/raw_access/raw_access", "utility/systemparam/systemparam", "video/pmf/pmf", "video/pmf_simple/pmf_simple", - - # Currently hang or crash. - "audio/atrac/atractest", ] # These don't even run (or run correctly) on the real PSP test_broken = [ - "sysmem/sysmem", - "mstick/mstick", ]