diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..439718c0e --- /dev/null +++ b/.clang-format @@ -0,0 +1,34 @@ +Language: Cpp +BasedOnStyle: WebKit +AccessModifierOffset: -4 +AlignAfterOpenBracket: true +AlignEscapedNewlinesLeft: true +AlignOperands: false +AlignTrailingComments: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 120 +ConstructorInitializerIndentWidth: 4 +IndentCaseLabels: false +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Left +SpaceAfterCStyleCast: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpacesInContainerLiterals: true +SpaceInEmptyParentheses: false +SpacesInAngles: false +Standard: Cpp11 +TabWidth: 4 +UseTab: ForIndentation diff --git a/.gitignore b/.gitignore index 76dc4a5c8..2241cd0aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /build +*.user* *~ *.swp diff --git a/CHANGES b/CHANGES index 4a37e4abe..bdc51e5e6 100644 --- a/CHANGES +++ b/CHANGES @@ -22,6 +22,8 @@ Features: - Holdable shortcut for rewinding one frame at a time - Ability to boot directly into the BIOS - Preliminary support for yanking out the game pak while a game is running + - Thumb-drive mode by putting a file called portable.ini in the same folder + - Configurable display driver, between software and OpenGL Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit @@ -48,6 +50,13 @@ Bugfixes: - GBA Video: Fix windows not affecting sprites - VFS: Fix line-reading to return proper values - GBA Memory: Fix load/store multiple video memory waitstates + - GBA: Fix timing of reading from timer registers + - Util: Allow loading IPS patches that grow the ROM + - GBA Audio: Fix sample order in audio channel 3 + - GBA Audio: Fix 8-bit writes to audio channel 3 frequency + - ARM7: ARMHotplugDetach should call deinit + - Qt: Fix window being too tall after exiting fullscreen + - Qt: Fix a missing va_end call in the log handler lambda within the GameController constructor Misc: - Qt: Handle saving input settings better - Debugger: Free watchpoints in addition to breakpoints @@ -81,6 +90,12 @@ Misc: - GBA Video: Refactor software renderer into separate files - ARM7: Add emulation for Undefined CPU mode - GBA: More accurate cycle estimation for ROM prefetch and flash save chips + - ARM7: Reduce the size of the Thumb instruction table + - GBA: Don't include GBACLIDebugger struct unless needed + - SDL: Clean up GL context + - GBA Audio: Implement audio reset for channels A/B + - GBA Hardware: Backport generic RTC source into core + - All: Proper handling of Unicode file paths 0.2.1: (2015-05-13) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 29bf2ae02..52f31e41a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,11 @@ cmake_minimum_required(VERSION 2.6) project(mGBA C) set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=c99") +if(NOT MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=c99") +else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146") +endif() set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger") set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support") @@ -103,7 +107,7 @@ else() endif() endif() -add_custom_target(version-info ALL touch ${CMAKE_SOURCE_DIR}/src/util/version.c.in +add_custom_target(version-info ALL ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR}/src/util/version.c.in COMMAND ${CMAKE_COMMAND} -DGIT_COMMIT=${GIT_COMMIT} -DGIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} @@ -118,6 +122,7 @@ add_custom_target(version-info ALL touch ${CMAKE_SOURCE_DIR}/src/util/version.c. include(${CMAKE_SOURCE_DIR}/version.cmake) list(APPEND UTIL_SRC ${CMAKE_BINARY_DIR}/version.c) +source_group("Generated sources" FILES ${CMAKE_BINARY_DIR}/version.c) # Advanced settings set(BUILD_LTO ON CACHE BOOL "Build with link-time optimization") @@ -163,7 +168,7 @@ find_feature(USE_MAGICK "MagickWand") if(WIN32) set(WIN32_VERSION "${LIB_VERSION_MAJOR},${LIB_VERSION_MINOR},${LIB_VERSION_PATCH}") add_definitions(-D_WIN32_WINNT=0x0600) - list(APPEND OS_LIB ws2_32) + list(APPEND OS_LIB ws2_32 shlwapi) list(APPEND VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/windows/*.c) source_group("Windows-specific code" FILES ${OS_SRC}) @@ -198,7 +203,7 @@ if(APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") endif() -if(NOT HAIKU) +if(NOT HAIKU AND NOT MSVC AND NOT PSP2) list(APPEND OS_LIB m) endif() @@ -376,6 +381,10 @@ foreach(FEATURE IN LISTS FEATURES) list(APPEND FEATURE_DEFINES "USE_${FEATURE}") endforeach() +source_group("Virtual files" FILES ${VFS_SRC}) +source_group("Extra features" FILES ${FEATURE_SRC}) +source_group("Third-party code" FILES ${THIRD_PARTY_SRC}) + # Binaries set(CORE_SRC ${ARM_SRC} @@ -470,6 +479,10 @@ if(PSP) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp ${CMAKE_BINARY_DIR}/psp) endif() +if(PSP2) + add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/psp2 ${CMAKE_BINARY_DIR}/psp2) +endif() + # Packaging set(CPACK_PACKAGE_VERSION ${VERSION_STRING}) set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR}) diff --git a/README.md b/README.md index 91fc00c2d..be79140fc 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Features - Qt and SDL ports for a heavy-weight and a light-weight frontend. - Local (same computer) link cable support. - Save type detection, even for flash memory size[[2]](#flashdetect). +- Support for cartridges with motion sensors and rumble (only usable with game controllers). - Real-time clock support, even without configuration. - A built-in BIOS implementation, and ability to load external BIOS files. - Turbo/fast-forward support by holding Tab. @@ -26,8 +27,9 @@ Features - Remappable controls for both keyboards and gamepads. - Loading from ZIP and 7z files. - IPS, UPS and BPS patch support. -- Game debugging via a command-line interface (not available with Qt port) and GDB remote support. +- Game debugging via a command-line interface (not available with Qt port) and GDB remote support, compatible with IDA Pro. - Configurable emulation rewinding. +- Support for loading and exporting GameShark and Action Replay snapshots. ### Planned features @@ -74,7 +76,7 @@ Controls are configurable in the menu. The default gamepad controls are mapped s Compiling --------- -Compiling requires using CMake 2.8.11 or newer. GCC and Clang are both known to work to compile mGBA, but Visual Studio 2013 and older are known not to work. To use CMake to build on a Unix-based system, the recommended commands are as follows: +Compiling requires using CMake 2.8.11 or newer. GCC and Clang are both known to work to compile mGBA, but Visual Studio 2013 and older are known not to work. Support for Visual Studio 2015 and newer is coming soon. To use CMake to build on a Unix-based system, the recommended commands are as follows: mkdir build cd build @@ -84,6 +86,26 @@ Compiling requires using CMake 2.8.11 or newer. GCC and Clang are both known to This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies that are installed will be automatically detected, and features that are disabled if the dependencies are not found will be shown after running the `cmake` command after warnings about being unable to find them. +#### Windows developer building + +To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MinGW-w64 Win32 Shell") and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time): + + pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2} + +Check out the source code by running this command: + + git clone https://github.com/mgba-emu/mgba.git + +Then finally build it by running these commands: + + cd mgba + mkdir build + cd build + cmake .. -G "MSYS Makefiles" + make + +Please note that this build of mGBA for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. + ### Dependencies mGBA has no hard dependencies, however, the following optional dependencies are required for specific features. The features will be disabled if the dependencies can't be found. @@ -104,7 +126,6 @@ Footnotes - OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5)) - Mosaic for transformed OBJs ([Bug #9](http://mgba.io/b/9)) - BIOS call RegisterRamReset is partially stubbed out ([Bug #141](http://mgba.io/b/141)) -- Audio channel reset flags ([Bug #142](http://mgba.io/b/142)) - Game Pak prefetch ([Bug #195](http://mgba.io/b/195)) - BIOS call Stop, for entering sleep mode ([Bug #199](http://mgba.io/b/199)) @@ -124,4 +145,5 @@ mGBA contains the following third-party libraries: - [inih](https://code.google.com/p/inih/), which is copyright © 2009 Brush Technology and used under a BSD 3-clause license. - [blip-buf](https://code.google.com/p/blip-buf/), which is copyright © 2003 – 2009 Shay Green and used under a Lesser GNU Public License. -- [LZMA SDK](http://www.7-zip.org/sdk.html), which is public doman. +- [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain. +- [MurmurHash3](https://code.google.com/p/smhasher/wiki/MurmurHash3) implementation by Austin Appleby, which is public domain. diff --git a/src/arm/arm.c b/src/arm/arm.c index 118383cd7..6f9ca2990 100644 --- a/src/arm/arm.c +++ b/src/arm/arm.c @@ -42,30 +42,29 @@ void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) { cpu->bankedSPSRs[oldBank] = cpu->spsr.packed; cpu->spsr.packed = cpu->bankedSPSRs[newBank]; - } cpu->privilegeMode = mode; } static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) { switch (mode) { - case MODE_USER: - case MODE_SYSTEM: - // No banked registers - return BANK_NONE; - case MODE_FIQ: - return BANK_FIQ; - case MODE_IRQ: - return BANK_IRQ; - case MODE_SUPERVISOR: - return BANK_SUPERVISOR; - case MODE_ABORT: - return BANK_ABORT; - case MODE_UNDEFINED: - return BANK_UNDEFINED; - default: - // This should be unreached - return BANK_NONE; + case MODE_USER: + case MODE_SYSTEM: + // No banked registers + return BANK_NONE; + case MODE_FIQ: + return BANK_FIQ; + case MODE_IRQ: + return BANK_IRQ; + case MODE_SUPERVISOR: + return BANK_SUPERVISOR; + case MODE_ABORT: + return BANK_ABORT; + case MODE_UNDEFINED: + return BANK_UNDEFINED; + default: + // This should be unreached + return BANK_NONE; } } @@ -108,7 +107,7 @@ void ARMHotplugDetach(struct ARMCore* cpu, size_t slot) { if (slot >= cpu->numComponents) { return; } - cpu->components[slot]->init(cpu, cpu->components[slot]); + cpu->components[slot]->deinit(cpu->components[slot]); } void ARMReset(struct ARMCore* cpu) { @@ -308,7 +307,7 @@ void ARMRunLoop(struct ARMCore* cpu) { } void ARMRunFake(struct ARMCore* cpu, uint32_t opcode) { - if (cpu->executionMode== MODE_ARM) { + if (cpu->executionMode == MODE_ARM) { cpu->gprs[ARM_PC] -= WORD_SIZE_ARM; } else { cpu->gprs[ARM_PC] -= WORD_SIZE_THUMB; diff --git a/src/arm/arm.h b/src/arm/arm.h index ec04f5d72..4a9fdba6a 100644 --- a/src/arm/arm.h +++ b/src/arm/arm.h @@ -101,8 +101,10 @@ struct ARMMemory { void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter); void (*store8)(struct ARMCore*, uint32_t address, int8_t value, int* cycleCounter); - uint32_t (*loadMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); - uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); + uint32_t (*loadMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); + uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); uint32_t* activeRegion; uint32_t activeMask; @@ -110,8 +112,7 @@ struct ARMMemory { uint32_t activeSeqCycles16; uint32_t activeNonseqCycles32; uint32_t activeNonseqCycles16; - uint32_t activeUncachedCycles32; - uint32_t activeUncachedCycles16; + int32_t (*stall)(struct ARMCore*, int32_t wait); void (*setActiveRegion)(struct ARMCore*, uint32_t address); }; diff --git a/src/arm/decoder-inlines.h b/src/arm/decoder-inlines.h index 24f81c0e4..ad108383c 100644 --- a/src/arm/decoder-inlines.h +++ b/src/arm/decoder-inlines.h @@ -13,11 +13,11 @@ #include #include -#define LOAD_CYCLES \ +#define LOAD_CYCLES \ info->iCycles = 1; \ info->nDataCycles = 1; -#define STORE_CYCLES \ +#define STORE_CYCLES \ info->sInstructionCycles = 0; \ info->nInstructionCycles = 1; \ info->nDataCycles = 1; diff --git a/src/arm/decoder-thumb.c b/src/arm/decoder-thumb.c index 6bf389508..d1eac9c7d 100644 --- a/src/arm/decoder-thumb.c +++ b/src/arm/decoder-thumb.c @@ -16,9 +16,9 @@ BODY; \ } -#define DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ +#define DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(NAME, MNEMONIC) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->op3.immediate = IMMEDIATE; \ + info->op3.immediate = (opcode >> 6) & 0x0007; \ info->op1.reg = opcode & 0x0007; \ info->op2.reg = (opcode >> 3) & 0x0007; \ info->affectsCPSR = 1; \ @@ -27,11 +27,11 @@ ARM_OPERAND_REGISTER_2 | \ ARM_OPERAND_IMMEDIATE_3;) -#define DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, CYCLES, WIDTH) \ +#define DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, MNEMONIC, CYCLES, WIDTH) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ info->op1.reg = opcode & 0x0007; \ info->memory.baseReg = (opcode >> 3) & 0x0007; \ - info->memory.offset.immediate = IMMEDIATE * WIDTH; \ + info->memory.offset.immediate = ((opcode >> 6) & 0x0007) * WIDTH; \ info->memory.width = (enum ARMMemoryAccessType) WIDTH; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ ARM_OPERAND_AFFECTED_1 | \ @@ -40,71 +40,53 @@ ARM_MEMORY_IMMEDIATE_OFFSET; \ CYCLES) -#define DEFINE_IMMEDIATE_5_DECODER_MEM_LOAD_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ - DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, LOAD_CYCLES, WIDTH) +DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(LSL1, LSL) +DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(LSR1, LSR) +DEFINE_IMMEDIATE_5_DECODER_DATA_THUMB(ASR1, ASR) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(LDR1, LDR, LOAD_CYCLES, 4) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(LDRB1, LDR, LOAD_CYCLES, 1) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(LDRH1, LDR, LOAD_CYCLES, 2) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(STR1, STR, STORE_CYCLES, 4) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(STRB1, STR, STORE_CYCLES, 1) +DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(STRH1, STR, STORE_CYCLES, 2) -#define DEFINE_IMMEDIATE_5_DECODER_MEM_STORE_THUMB(NAME, IMMEDIATE, MNEMONIC, WIDTH) \ - DEFINE_IMMEDIATE_5_DECODER_MEM_THUMB(NAME, IMMEDIATE, MNEMONIC, STORE_CYCLES, WIDTH) - -#define DEFINE_IMMEDIATE_5_DECODER_THUMB(NAME, MNEMONIC, TYPE, WIDTH) \ - COUNT_CALL_5(DEFINE_IMMEDIATE_5_DECODER_ ## TYPE ## _THUMB, NAME ## _, MNEMONIC, WIDTH) - -DEFINE_IMMEDIATE_5_DECODER_THUMB(LSL1, LSL, DATA,) -DEFINE_IMMEDIATE_5_DECODER_THUMB(LSR1, LSR, DATA,) -DEFINE_IMMEDIATE_5_DECODER_THUMB(ASR1, ASR, DATA,) -DEFINE_IMMEDIATE_5_DECODER_THUMB(LDR1, LDR, MEM_LOAD, 4) -DEFINE_IMMEDIATE_5_DECODER_THUMB(LDRB1, LDR, MEM_LOAD, 1) -DEFINE_IMMEDIATE_5_DECODER_THUMB(LDRH1, LDR, MEM_LOAD, 2) -DEFINE_IMMEDIATE_5_DECODER_THUMB(STR1, STR, MEM_STORE, 4) -DEFINE_IMMEDIATE_5_DECODER_THUMB(STRB1, STR, MEM_STORE, 1) -DEFINE_IMMEDIATE_5_DECODER_THUMB(STRH1, STR, MEM_STORE, 2) - -#define DEFINE_DATA_FORM_1_DECODER_EX_THUMB(NAME, RM, MNEMONIC) \ +#define DEFINE_DATA_FORM_1_DECODER_THUMB(NAME, MNEMONIC) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ info->op1.reg = opcode & 0x0007; \ info->op2.reg = (opcode >> 3) & 0x0007; \ - info->op3.reg = RM; \ + info->op3.reg = (opcode >> 6) & 0x0007; \ info->affectsCPSR = 1; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ ARM_OPERAND_AFFECTED_1 | \ ARM_OPERAND_REGISTER_2 | \ ARM_OPERAND_REGISTER_3;) -#define DEFINE_DATA_FORM_1_DECODER_THUMB(NAME) \ - COUNT_CALL_3(DEFINE_DATA_FORM_1_DECODER_EX_THUMB, NAME ## 3_R, NAME) +DEFINE_DATA_FORM_1_DECODER_THUMB(ADD3, ADD) +DEFINE_DATA_FORM_1_DECODER_THUMB(SUB3, SUB) -DEFINE_DATA_FORM_1_DECODER_THUMB(ADD) -DEFINE_DATA_FORM_1_DECODER_THUMB(SUB) - -#define DEFINE_DATA_FORM_2_DECODER_EX_THUMB(NAME, IMMEDIATE, MNEMONIC) \ +#define DEFINE_DATA_FORM_2_DECODER_THUMB(NAME, MNEMONIC) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ info->op1.reg = opcode & 0x0007; \ info->op2.reg = (opcode >> 3) & 0x0007; \ - info->op3.immediate = IMMEDIATE; \ + info->op3.immediate = (opcode >> 6) & 0x0007; \ info->affectsCPSR = 1; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ ARM_OPERAND_AFFECTED_1 | \ ARM_OPERAND_REGISTER_2 | \ ARM_OPERAND_IMMEDIATE_3;) -#define DEFINE_DATA_FORM_2_DECODER_THUMB(NAME) \ - COUNT_CALL_3(DEFINE_DATA_FORM_2_DECODER_EX_THUMB, NAME ## 1_, NAME) +DEFINE_DATA_FORM_2_DECODER_THUMB(ADD1, ADD) +DEFINE_DATA_FORM_2_DECODER_THUMB(SUB1, SUB) -DEFINE_DATA_FORM_2_DECODER_THUMB(ADD) -DEFINE_DATA_FORM_2_DECODER_THUMB(SUB) - -#define DEFINE_DATA_FORM_3_DECODER_EX_THUMB(NAME, RD, MNEMONIC, AFFECTED) \ +#define DEFINE_DATA_FORM_3_DECODER_THUMB(NAME, MNEMONIC, AFFECTED) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->op1.reg = RD; \ + info->op1.reg = (opcode >> 8) & 0x0007; \ info->op2.immediate = opcode & 0x00FF; \ info->affectsCPSR = 1; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ AFFECTED | \ ARM_OPERAND_IMMEDIATE_2;) -#define DEFINE_DATA_FORM_3_DECODER_THUMB(NAME, MNEMONIC, AFFECTED) \ - COUNT_CALL_3(DEFINE_DATA_FORM_3_DECODER_EX_THUMB, NAME ## _R, MNEMONIC, AFFECTED) - DEFINE_DATA_FORM_3_DECODER_THUMB(ADD2, ADD, ARM_OPERAND_AFFECTED_1) DEFINE_DATA_FORM_3_DECODER_THUMB(CMP1, CMP, ARM_OPERAND_NONE) DEFINE_DATA_FORM_3_DECODER_THUMB(MOV1, MOV, ARM_OPERAND_AFFECTED_1) @@ -159,9 +141,9 @@ DEFINE_DECODER_WITH_HIGH_THUMB(ADD4, ADD, ARM_OPERAND_AFFECTED_1, 0) DEFINE_DECODER_WITH_HIGH_THUMB(CMP3, CMP, ARM_OPERAND_NONE, 1) DEFINE_DECODER_WITH_HIGH_THUMB(MOV3, MOV, ARM_OPERAND_AFFECTED_1, 0) -#define DEFINE_IMMEDIATE_WITH_REGISTER_DATA_THUMB(NAME, RD, MNEMONIC, REG) \ +#define DEFINE_IMMEDIATE_WITH_REGISTER_DATA_THUMB(NAME, MNEMONIC, REG) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->op1.reg = RD; \ + info->op1.reg = (opcode >> 6) & 0x0007; \ info->op2.reg = REG; \ info->op3.immediate = (opcode & 0x00FF) << 2; \ info->operandFormat = ARM_OPERAND_REGISTER_1 | \ @@ -169,9 +151,9 @@ DEFINE_DECODER_WITH_HIGH_THUMB(MOV3, MOV, ARM_OPERAND_AFFECTED_1, 0) ARM_OPERAND_REGISTER_2 | \ ARM_OPERAND_IMMEDIATE_3;) -#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, CYCLES) \ +#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, MNEMONIC, REG, CYCLES) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->op1.reg = RD; \ + info->op1.reg = (opcode >> 6) & 0x0007; \ info->memory.baseReg = REG; \ info->memory.offset.immediate = (opcode & 0x00FF) << 2; \ info->memory.width = ARM_ACCESS_WORD; \ @@ -182,25 +164,16 @@ DEFINE_DECODER_WITH_HIGH_THUMB(MOV3, MOV, ARM_OPERAND_AFFECTED_1, 0) ARM_MEMORY_IMMEDIATE_OFFSET; \ CYCLES;) -#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_LOAD_THUMB(NAME, RD, MNEMONIC, REG) \ - DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, LOAD_CYCLES) +DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(LDR3, LDR, ARM_PC, LOAD_CYCLES) +DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(LDR4, LDR, ARM_SP, LOAD_CYCLES) +DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(STR3, STR, ARM_SP, STORE_CYCLES) -#define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_STORE_THUMB(NAME, RD, MNEMONIC, REG) \ - DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, RD, MNEMONIC, REG, STORE_CYCLES) +DEFINE_IMMEDIATE_WITH_REGISTER_DATA_THUMB(ADD5, ADD, ARM_PC) +DEFINE_IMMEDIATE_WITH_REGISTER_DATA_THUMB(ADD6, ADD, ARM_SP) -#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, MNEMONIC, TYPE, REG) \ - COUNT_CALL_3(DEFINE_IMMEDIATE_WITH_REGISTER_ ## TYPE ## _THUMB, NAME ## _R, MNEMONIC, REG) - -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, LDR, MEM_LOAD, ARM_PC) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, LDR, MEM_LOAD, ARM_SP) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, STR, MEM_STORE, ARM_SP) - -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD5, ADD, DATA, ARM_PC) -DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, ADD, DATA, ARM_SP) - -#define DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB(NAME, RM, MNEMONIC, CYCLES, TYPE) \ +#define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, MNEMONIC, CYCLES, TYPE) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->memory.offset.reg = RM; \ + info->memory.offset.reg = (opcode >> 6) & 0x0007; \ info->op1.reg = opcode & 0x0007; \ info->memory.baseReg = (opcode >> 3) & 0x0007; \ info->memory.width = TYPE; \ @@ -211,9 +184,6 @@ DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, ADD, DATA, ARM_SP) ARM_MEMORY_REGISTER_OFFSET; \ CYCLES;) -#define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, MNEMONIC, CYCLES, TYPE) \ - COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, MNEMONIC, CYCLES, TYPE) - DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, LDR, LOAD_CYCLES, ARM_ACCESS_WORD) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, LDR, LOAD_CYCLES, ARM_ACCESS_BYTE) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, LDR, LOAD_CYCLES, ARM_ACCESS_HALFWORD) @@ -237,7 +207,7 @@ DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, STR, STORE_CYCLES, ARM_ACCESS_HALFW DIRECTION;) #define DEFINE_LOAD_STORE_MULTIPLE_THUMB(NAME) \ - COUNT_CALL_3(DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB, NAME ## IA_R, NAME, ARM_MEMORY_INCREMENT_AFTER, 0) + DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(NAME ## IA, (opcode >> 8) & 0x0007, NAME, ARM_MEMORY_INCREMENT_AFTER, 0) DEFINE_LOAD_STORE_MULTIPLE_THUMB(LDM) DEFINE_LOAD_STORE_MULTIPLE_THUMB(STM) diff --git a/src/arm/decoder.h b/src/arm/decoder.h index 57187dd13..a1dd59dbc 100644 --- a/src/arm/decoder.h +++ b/src/arm/decoder.h @@ -209,7 +209,8 @@ struct ARMInstructionInfo { void ARMDecodeARM(uint32_t opcode, struct ARMInstructionInfo* info); void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info); -bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2, struct ARMInstructionInfo* out); +bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2, + struct ARMInstructionInfo* out); int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen); #endif diff --git a/src/arm/emitter-inlines.h b/src/arm/emitter-inlines.h index b2e37de97..38bab70bd 100644 --- a/src/arm/emitter-inlines.h +++ b/src/arm/emitter-inlines.h @@ -29,105 +29,4 @@ LEFT, \ RIGHT -#define APPLY(F, ...) F(__VA_ARGS__) - -#define COUNT_CALL_1(EMITTER, PREFIX, ...) \ - EMITTER(PREFIX ## 0, 0, __VA_ARGS__) \ - EMITTER(PREFIX ## 1, 1, __VA_ARGS__) - -#define COUNT_CALL_2(EMITTER, PREFIX, ...) \ - COUNT_CALL_1(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 2, 2, __VA_ARGS__) \ - EMITTER(PREFIX ## 3, 3, __VA_ARGS__) - -#define COUNT_CALL_3(EMITTER, PREFIX, ...) \ - COUNT_CALL_2(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 4, 4, __VA_ARGS__) \ - EMITTER(PREFIX ## 5, 5, __VA_ARGS__) \ - EMITTER(PREFIX ## 6, 6, __VA_ARGS__) \ - EMITTER(PREFIX ## 7, 7, __VA_ARGS__) - -#define COUNT_CALL_4(EMITTER, PREFIX, ...) \ - COUNT_CALL_3(EMITTER, PREFIX, __VA_ARGS__) \ - EMITTER(PREFIX ## 8, 8, __VA_ARGS__) \ - EMITTER(PREFIX ## 9, 9, __VA_ARGS__) \ - EMITTER(PREFIX ## A, 10, __VA_ARGS__) \ - EMITTER(PREFIX ## B, 11, __VA_ARGS__) \ - EMITTER(PREFIX ## C, 12, __VA_ARGS__) \ - EMITTER(PREFIX ## D, 13, __VA_ARGS__) \ - EMITTER(PREFIX ## E, 14, __VA_ARGS__) \ - EMITTER(PREFIX ## F, 15, __VA_ARGS__) - -#define COUNT_CALL_5(EMITTER, PREFIX, ...) \ - COUNT_CALL_4(EMITTER, PREFIX ## 0, __VA_ARGS__) \ - EMITTER(PREFIX ## 10, 16, __VA_ARGS__) \ - EMITTER(PREFIX ## 11, 17, __VA_ARGS__) \ - EMITTER(PREFIX ## 12, 18, __VA_ARGS__) \ - EMITTER(PREFIX ## 13, 19, __VA_ARGS__) \ - EMITTER(PREFIX ## 14, 20, __VA_ARGS__) \ - EMITTER(PREFIX ## 15, 21, __VA_ARGS__) \ - EMITTER(PREFIX ## 16, 22, __VA_ARGS__) \ - EMITTER(PREFIX ## 17, 23, __VA_ARGS__) \ - EMITTER(PREFIX ## 18, 24, __VA_ARGS__) \ - EMITTER(PREFIX ## 19, 25, __VA_ARGS__) \ - EMITTER(PREFIX ## 1A, 26, __VA_ARGS__) \ - EMITTER(PREFIX ## 1B, 27, __VA_ARGS__) \ - EMITTER(PREFIX ## 1C, 28, __VA_ARGS__) \ - EMITTER(PREFIX ## 1D, 29, __VA_ARGS__) \ - EMITTER(PREFIX ## 1E, 30, __VA_ARGS__) \ - EMITTER(PREFIX ## 1F, 31, __VA_ARGS__) \ - -#define COUNT_1(EMITTER, PREFIX) \ - EMITTER(PREFIX ## 0) \ - EMITTER(PREFIX ## 1) - -#define COUNT_2(EMITTER, PREFIX) \ - COUNT_1(EMITTER, PREFIX) \ - EMITTER(PREFIX ## 2) \ - EMITTER(PREFIX ## 3) - -#define COUNT_3(EMITTER, PREFIX) \ - COUNT_2(EMITTER, PREFIX) \ - EMITTER(PREFIX ## 4) \ - EMITTER(PREFIX ## 5) \ - EMITTER(PREFIX ## 6) \ - EMITTER(PREFIX ## 7) - -#define COUNT_4(EMITTER, PREFIX) \ - COUNT_3(EMITTER, PREFIX) \ - EMITTER(PREFIX ## 8) \ - EMITTER(PREFIX ## 9) \ - EMITTER(PREFIX ## A) \ - EMITTER(PREFIX ## B) \ - EMITTER(PREFIX ## C) \ - EMITTER(PREFIX ## D) \ - EMITTER(PREFIX ## E) \ - EMITTER(PREFIX ## F) - -#define COUNT_5(EMITTER, PREFIX) \ - COUNT_4(EMITTER, PREFIX ## 0) \ - EMITTER(PREFIX ## 10) \ - EMITTER(PREFIX ## 11) \ - EMITTER(PREFIX ## 12) \ - EMITTER(PREFIX ## 13) \ - EMITTER(PREFIX ## 14) \ - EMITTER(PREFIX ## 15) \ - EMITTER(PREFIX ## 16) \ - EMITTER(PREFIX ## 17) \ - EMITTER(PREFIX ## 18) \ - EMITTER(PREFIX ## 19) \ - EMITTER(PREFIX ## 1A) \ - EMITTER(PREFIX ## 1B) \ - EMITTER(PREFIX ## 1C) \ - EMITTER(PREFIX ## 1D) \ - EMITTER(PREFIX ## 1E) \ - EMITTER(PREFIX ## 1F) \ - -#define ECHO(...) __VA_ARGS__, -#define ECHO_4(...) \ - ECHO(__VA_ARGS__) \ - ECHO(__VA_ARGS__) \ - ECHO(__VA_ARGS__) \ - ECHO(__VA_ARGS__) - #endif diff --git a/src/arm/emitter-thumb.h b/src/arm/emitter-thumb.h index 138784c97..7d27524b2 100644 --- a/src/arm/emitter-thumb.h +++ b/src/arm/emitter-thumb.h @@ -18,17 +18,17 @@ DECLARE_INSTRUCTION_THUMB(EMITTER, NAME ## 11) #define DECLARE_THUMB_EMITTER_BLOCK(EMITTER) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LSL1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LSR1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ASR1_)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD3_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB3_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD1_)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB1_)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, MOV1_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, CMP1_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD2_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, SUB2_R)) \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LSL1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LSR1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ASR1))), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ADD3)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, SUB3)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, ADD1)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, SUB1)), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, MOV1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, CMP1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ADD2))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, SUB2))), \ DECLARE_INSTRUCTION_THUMB(EMITTER, AND), \ DECLARE_INSTRUCTION_THUMB(EMITTER, EOR), \ DECLARE_INSTRUCTION_THUMB(EMITTER, LSL2), \ @@ -52,25 +52,25 @@ DECLARE_INSTRUCTION_THUMB(EMITTER, BX), \ DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ DECLARE_INSTRUCTION_THUMB(EMITTER, ILL), \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR3_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STR2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSB_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB2_R)) \ - APPLY(COUNT_3, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSH_R)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STR1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRB1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, STRH1_)) \ - APPLY(COUNT_5, ECHO, DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH1_)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STR3_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDR4_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD5_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, ADD6_R)) \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDR3))), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, STR2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, STRH2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, STRB2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSB)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, LDR2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB2)), \ + DO_8(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRSH)), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, STR1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDR1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, STRB1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRB1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, STRH1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDRH1))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, STR3))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDR4))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ADD5))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ADD6))), \ DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ DECLARE_INSTRUCTION_THUMB(EMITTER, ADD7), \ DECLARE_INSTRUCTION_THUMB(EMITTER, SUB4), \ @@ -87,8 +87,8 @@ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, POPR)), \ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BKPT)), \ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, ILL)), \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, STMIA_R)) \ - APPLY(COUNT_3, ECHO_4, DECLARE_INSTRUCTION_THUMB(EMITTER, LDMIA_R)) \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, STMIA))), \ + DO_8(DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, LDMIA))), \ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BEQ)), \ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BNE)), \ DO_4(DECLARE_INSTRUCTION_THUMB(EMITTER, BCS)), \ diff --git a/src/arm/isa-arm.c b/src/arm/isa-arm.c index ca4327c6e..83b59cc70 100644 --- a/src/arm/isa-arm.c +++ b/src/arm/isa-arm.c @@ -259,7 +259,7 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) { #define ADDR_MODE_4_WRITEBACK_STM cpu->gprs[rn] = address; #define ARM_LOAD_POST_BODY \ - currentCycles += 1 + cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \ + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \ if (rd == ARM_PC) { \ ARM_WRITE_PC; \ } @@ -567,7 +567,7 @@ DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRT, DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM, load, - currentCycles += 1 + cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; + currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; if (rs & 0x8000) { ARM_WRITE_PC; }) diff --git a/src/arm/isa-arm.h b/src/arm/isa-arm.h index 2ee595078..4058a1958 100644 --- a/src/arm/isa-arm.h +++ b/src/arm/isa-arm.h @@ -15,5 +15,4 @@ struct ARMCore; typedef void (*ARMInstruction)(struct ARMCore*, uint32_t opcode); const ARMInstruction _armTable[0x1000]; - #endif diff --git a/src/arm/isa-inlines.h b/src/arm/isa-inlines.h index f9a8daf9d..cdf6f9cba 100644 --- a/src/arm/isa-inlines.h +++ b/src/arm/isa-inlines.h @@ -35,35 +35,39 @@ #define ARM_V_ADDITION(M, N, D) (!(ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))) && (ARM_SIGN((N) ^ (D)))) #define ARM_V_SUBTRACTION(M, N, D) ((ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D)))) -#define ARM_WAIT_MUL(R) \ - if ((R & 0xFFFFFF00) == 0xFFFFFF00 || !(R & 0xFFFFFF00)) { \ - currentCycles += 1; \ - } else if ((R & 0xFFFF0000) == 0xFFFF0000 || !(R & 0xFFFF0000)) { \ - currentCycles += 2; \ - } else if ((R & 0xFF000000) == 0xFF000000 || !(R & 0xFF000000)) { \ - currentCycles += 3; \ - } else { \ - currentCycles += 4; \ +#define ARM_WAIT_MUL(R) \ + { \ + int32_t wait; \ + if ((R & 0xFFFFFF00) == 0xFFFFFF00 || !(R & 0xFFFFFF00)) { \ + wait = 1; \ + } else if ((R & 0xFFFF0000) == 0xFFFF0000 || !(R & 0xFFFF0000)) { \ + wait = 2; \ + } else if ((R & 0xFF000000) == 0xFF000000 || !(R & 0xFF000000)) { \ + wait = 3; \ + } else { \ + wait = 4; \ + } \ + currentCycles += cpu->memory.stall(cpu, wait); \ } #define ARM_STUB cpu->irqh.hitStub(cpu, opcode) #define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode) -#define ARM_WRITE_PC \ - cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \ - cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ +#define ARM_WRITE_PC \ + cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \ + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ - cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \ + cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \ LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ - currentCycles += 2 + cpu->memory.activeUncachedCycles32 + cpu->memory.activeSeqCycles32; + currentCycles += 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32; -#define THUMB_WRITE_PC \ - cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \ - cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ +#define THUMB_WRITE_PC \ + cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \ + cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \ LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ - cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \ + cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \ LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \ - currentCycles += 2 + cpu->memory.activeUncachedCycles16 + cpu->memory.activeSeqCycles16; + currentCycles += 2 + cpu->memory.activeNonseqCycles16 + cpu->memory.activeSeqCycles16; static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) { return mode != MODE_SYSTEM && mode != MODE_USER; diff --git a/src/arm/isa-thumb.c b/src/arm/isa-thumb.c index 4107315ec..4132303fa 100644 --- a/src/arm/isa-thumb.c +++ b/src/arm/isa-thumb.c @@ -42,7 +42,7 @@ #define THUMB_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles16) #define THUMB_LOAD_POST_BODY \ - currentCycles += 1 + cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16; + currentCycles += cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16; #define THUMB_STORE_POST_BODY \ currentCycles += cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16; @@ -54,16 +54,13 @@ cpu->cycles += currentCycles; \ } -#define DEFINE_IMMEDIATE_5_INSTRUCTION_EX_THUMB(NAME, IMMEDIATE, BODY) \ +#define DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(NAME, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \ - int immediate = IMMEDIATE; \ + int immediate = (opcode >> 6) & 0x001F; \ int rd = opcode & 0x0007; \ int rm = (opcode >> 3) & 0x0007; \ BODY;) -#define DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_CALL_5(DEFINE_IMMEDIATE_5_INSTRUCTION_EX_THUMB, NAME ## _, BODY) - DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSL1, if (!immediate) { cpu->gprs[rd] = cpu->gprs[rm]; @@ -104,40 +101,31 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;) DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;) -#define DEFINE_DATA_FORM_1_INSTRUCTION_EX_THUMB(NAME, RM, BODY) \ - DEFINE_INSTRUCTION_THUMB(NAME, \ - int rm = RM; \ - int rd = opcode & 0x0007; \ - int rn = (opcode >> 3) & 0x0007; \ - BODY;) - #define DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_CALL_3(DEFINE_DATA_FORM_1_INSTRUCTION_EX_THUMB, NAME ## 3_R, BODY) - -DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(ADD, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm])) -DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm])) - -#define DEFINE_DATA_FORM_2_INSTRUCTION_EX_THUMB(NAME, IMMEDIATE, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \ - int immediate = IMMEDIATE; \ + int rm = (opcode >> 6) & 0x0007; \ int rd = opcode & 0x0007; \ int rn = (opcode >> 3) & 0x0007; \ BODY;) +DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(ADD3, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm])) +DEFINE_DATA_FORM_1_INSTRUCTION_THUMB(SUB3, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], cpu->gprs[rm])) + #define DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_CALL_3(DEFINE_DATA_FORM_2_INSTRUCTION_EX_THUMB, NAME ## 1_, BODY) - -DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(ADD, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], immediate)) -DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(SUB, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], immediate)) - -#define DEFINE_DATA_FORM_3_INSTRUCTION_EX_THUMB(NAME, RD, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \ - int rd = RD; \ - int immediate = opcode & 0x00FF; \ + int immediate = (opcode >> 6) & 0x0007; \ + int rd = opcode & 0x0007; \ + int rn = (opcode >> 3) & 0x0007; \ BODY;) +DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(ADD1, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rn], immediate)) +DEFINE_DATA_FORM_2_INSTRUCTION_THUMB(SUB1, THUMB_SUBTRACTION(cpu->gprs[rd], cpu->gprs[rn], immediate)) + #define DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(NAME, BODY) \ - COUNT_CALL_3(DEFINE_DATA_FORM_3_INSTRUCTION_EX_THUMB, NAME ## _R, BODY) + DEFINE_INSTRUCTION_THUMB(NAME, \ + int rd = (opcode >> 8) & 0x0007; \ + int immediate = opcode & 0x00FF; \ + BODY;) DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(ADD2, THUMB_ADDITION(cpu->gprs[rd], cpu->gprs[rd], immediate)) DEFINE_DATA_FORM_3_INSTRUCTION_THUMB(CMP1, int aluOut = cpu->gprs[rd] - immediate; THUMB_SUBTRACTION_S(cpu->gprs[rd], immediate, aluOut)) @@ -260,15 +248,12 @@ DEFINE_INSTRUCTION_WITH_HIGH_THUMB(MOV3, THUMB_WRITE_PC; }) -#define DEFINE_IMMEDIATE_WITH_REGISTER_EX_THUMB(NAME, RD, BODY) \ +#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \ - int rd = RD; \ + int rd = (opcode >> 8) & 0x0007; \ int immediate = (opcode & 0x00FF) << 2; \ BODY;) -#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \ - COUNT_CALL_3(DEFINE_IMMEDIATE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) - DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR3, cpu->gprs[rd] = cpu->memory.load32(cpu, (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;) DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[ARM_SP] + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;) DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory.store32(cpu, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;) @@ -276,16 +261,13 @@ DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory.store32(cpu, cpu->gprs[AR DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD5, cpu->gprs[rd] = (cpu->gprs[ARM_PC] & 0xFFFFFFFC) + immediate) DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, cpu->gprs[rd] = cpu->gprs[ARM_SP] + immediate) -#define DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB(NAME, RM, BODY) \ +#define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, BODY) \ DEFINE_INSTRUCTION_THUMB(NAME, \ - int rm = RM; \ + int rm = (opcode >> 6) & 0x0007; \ int rd = opcode & 0x0007; \ int rn = (opcode >> 3) & 0x0007; \ BODY;) -#define DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(NAME, BODY) \ - COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY) - DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;) @@ -295,7 +277,7 @@ DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[r DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;) DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;) -#define DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(NAME, RN, LS, DIRECTION, PRE_BODY, WRITEBACK) \ +#define DEFINE_LOAD_STORE_MULTIPLE_THUMB(NAME, RN, LS, DIRECTION, PRE_BODY, WRITEBACK) \ DEFINE_INSTRUCTION_THUMB(NAME, \ int rn = RN; \ UNUSED(rn); \ @@ -305,20 +287,21 @@ DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[ address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤tCycles); \ WRITEBACK;) -#define DEFINE_LOAD_STORE_MULTIPLE_THUMB(NAME, LS, DIRECTION, WRITEBACK) \ - COUNT_CALL_3(DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB, NAME ## _R, LS, DIRECTION, , WRITEBACK) - DEFINE_LOAD_STORE_MULTIPLE_THUMB(LDMIA, + (opcode >> 8) & 0x0007, load, IA, + , THUMB_LOAD_POST_BODY; if (!((1 << rn) & rs)) { cpu->gprs[rn] = address; }) DEFINE_LOAD_STORE_MULTIPLE_THUMB(STMIA, + (opcode >> 8) & 0x0007, store, IA, + , THUMB_STORE_POST_BODY; cpu->gprs[rn] = address;) @@ -348,7 +331,7 @@ DEFINE_CONDITIONAL_BRANCH_THUMB(LE) DEFINE_INSTRUCTION_THUMB(ADD7, cpu->gprs[ARM_SP] += (opcode & 0x7F) << 2) DEFINE_INSTRUCTION_THUMB(SUB4, cpu->gprs[ARM_SP] -= (opcode & 0x7F) << 2) -DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POP, +DEFINE_LOAD_STORE_MULTIPLE_THUMB(POP, ARM_SP, load, IA, @@ -356,7 +339,7 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POP, THUMB_LOAD_POST_BODY; cpu->gprs[ARM_SP] = address) -DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POPR, +DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPR, ARM_SP, load, IA, @@ -365,7 +348,7 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(POPR, cpu->gprs[ARM_SP] = address; THUMB_WRITE_PC;) -DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSH, +DEFINE_LOAD_STORE_MULTIPLE_THUMB(PUSH, ARM_SP, store, DB, @@ -373,7 +356,7 @@ DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSH, THUMB_STORE_POST_BODY; cpu->gprs[ARM_SP] = address) -DEFINE_LOAD_STORE_MULTIPLE_EX_THUMB(PUSHR, +DEFINE_LOAD_STORE_MULTIPLE_THUMB(PUSHR, ARM_SP, store, DB, diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 1da5207d1..90d657af1 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -82,7 +82,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "print/t", _printBin, CLIDVParse, "Print a value as binary" }, { "print/x", _printHex, CLIDVParse, "Print a value as hexadecimal" }, { "q", _quit, 0, "Quit the emulator" }, - { "quit", _quit, 0, "Quit the emulator" }, + { "quit", _quit, 0, "Quit the emulator" }, { "reset", _reset, 0, "Reset the emulation" }, { "r/1", _readByte, CLIDVParse, "Read a byte from a specified offset" }, { "r/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" }, @@ -103,13 +103,13 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { static inline void _printPSR(union PSR psr) { printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed, - psr.n ? 'N' : '-', - psr.z ? 'Z' : '-', - psr.c ? 'C' : '-', - psr.v ? 'V' : '-', - psr.i ? 'I' : '-', - psr.f ? 'F' : '-', - psr.t ? 'T' : '-'); + psr.n ? 'N' : '-', + psr.z ? 'Z' : '-', + psr.c ? 'C' : '-', + psr.v ? 'V' : '-', + psr.i ? 'I' : '-', + psr.f ? 'F' : '-', + psr.t ? 'T' : '-'); } static void _handleDeath(int sig) { @@ -196,7 +196,7 @@ static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(debugger); - for ( ; dv; dv = dv->next) { + for (; dv; dv = dv->next) { printf(" %u", dv->intValue); } printf("\n"); @@ -204,7 +204,7 @@ static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(debugger); - for ( ; dv; dv = dv->next) { + for (; dv; dv = dv->next) { printf(" 0b"); int i = 32; while (i--) { @@ -216,7 +216,7 @@ static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { static void _printHex(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(debugger); - for ( ; dv; dv = dv->next) { + for (; dv; dv = dv->next) { printf(" 0x%08X", dv->intValue); } printf("\n"); @@ -289,10 +289,10 @@ static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv int r; for (r = 0; r < 4; ++r) { printf("%08X %08X %08X %08X\n", - debugger->d.cpu->gprs[r << 2], - debugger->d.cpu->gprs[(r << 2) + 1], - debugger->d.cpu->gprs[(r << 2) + 2], - debugger->d.cpu->gprs[(r << 2) + 3]); + debugger->d.cpu->gprs[r << 2], + debugger->d.cpu->gprs[(r << 2) + 1], + debugger->d.cpu->gprs[(r << 2) + 2], + debugger->d.cpu->gprs[(r << 2) + 3]); } _printPSR(debugger->d.cpu->cpsr); int instructionLength; diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index db474a7d2..09f021805 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -108,7 +108,7 @@ static uint32_t _hex2int(const char* hex, int maxDigits) { letter = *hex - '0'; if (letter > 9) { letter = *hex - 'a'; - if (letter > 5) { + if (letter > 5) { break; } value *= 0x10; diff --git a/src/debugger/memory-debugger.c b/src/debugger/memory-debugger.c index 3043929ff..e4d880a5b 100644 --- a/src/debugger/memory-debugger.c +++ b/src/debugger/memory-debugger.c @@ -29,14 +29,14 @@ static uint32_t _popcount32(unsigned bits) { } \ } -#define CREATE_SHIM(NAME, RETURN, TYPES, ARGS...) \ +#define CREATE_SHIM(NAME, RETURN, TYPES, ...) \ static RETURN ARMDebuggerShim_ ## NAME TYPES { \ struct ARMDebugger* debugger; \ FIND_DEBUGGER(debugger, cpu); \ - return debugger->originalMemory.NAME(cpu, ARGS); \ + return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \ } -#define CREATE_WATCHPOINT_SHIM(NAME, WIDTH, RETURN, TYPES, ARGS...) \ +#define CREATE_WATCHPOINT_SHIM(NAME, WIDTH, RETURN, TYPES, ...) \ static RETURN ARMDebuggerShim_ ## NAME TYPES { \ struct ARMDebugger* debugger; \ FIND_DEBUGGER(debugger, cpu); \ @@ -44,7 +44,7 @@ static uint32_t _popcount32(unsigned bits) { if (_checkWatchpoints(debugger, address, &info, WIDTH)) { \ ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \ } \ - return debugger->originalMemory.NAME(cpu, ARGS); \ + return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \ } #define CREATE_MULTIPLE_WATCHPOINT_SHIM(NAME) \ diff --git a/src/debugger/parser.c b/src/debugger/parser.c index e6fa97e0b..5d822598d 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -118,8 +118,8 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { break; case LEX_EXPECT_BINARY: switch (token) { - case '0': - case '1': + case '0': + case '1': // TODO: handle overflow next <<= 1; next += token - '0'; diff --git a/src/gba/audio.c b/src/gba/audio.c index 26e9ac3b5..372c7ef25 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -41,7 +41,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { audio->left = blip_new(BLIP_BUFFER_SIZE); audio->right = blip_new(BLIP_BUFFER_SIZE); // Guess too large; we hang producing extra samples if we guess too low - blip_set_rates(audio->left, GBA_ARM7TDMI_FREQUENCY, 96000); + blip_set_rates(audio->left, GBA_ARM7TDMI_FREQUENCY, 96000); blip_set_rates(audio->right, GBA_ARM7TDMI_FREQUENCY, 96000); #endif CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); @@ -456,7 +456,12 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) { audio->chBRight = GBARegisterSOUNDCNT_HIGetChBRight(value); audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value); audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value); - // TODO: Implement channel reset + if (GBARegisterSOUNDCNT_HIIsChAReset(value)) { + CircleBufferClear(&audio->chA.fifo); + } + if (GBARegisterSOUNDCNT_HIIsChBReset(value)) { + CircleBufferClear(&audio->chB.fifo); + } } void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { @@ -699,15 +704,15 @@ static int32_t _updateChannel3(struct GBAAudioChannel3* ch) { start = 3; end = 0; } - uint32_t bitsCarry = ch->wavedata[end] & 0x0F000000; + uint32_t bitsCarry = ch->wavedata[end] & 0x000000F0; uint32_t bits; for (i = start; i >= end; --i) { - bits = ch->wavedata[i] & 0x0F000000; - ch->wavedata[i] = ((ch->wavedata[i] & 0xF0F0F0F0) >> 4) | ((ch->wavedata[i] & 0x000F0F0F) << 12); - ch->wavedata[i] |= bitsCarry >> 20; + bits = ch->wavedata[i] & 0x000000F0; + ch->wavedata[i] = ((ch->wavedata[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata[i] & 0xF0F0F000) >> 12); + ch->wavedata[i] |= bitsCarry << 20; bitsCarry = bits; } - ch->sample = bitsCarry >> 24; + ch->sample = bitsCarry >> 4; ch->sample -= 8; ch->sample *= volume * 4; return 8 * (2048 - ch->control.rate); diff --git a/src/gba/audio.h b/src/gba/audio.h index dc16d4631..e236e01a3 100644 --- a/src/gba/audio.h +++ b/src/gba/audio.h @@ -279,7 +279,8 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles); #if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned nSamples); -unsigned GBAAudioResampleNN(struct GBAAudio*, float ratio, float* drift, struct GBAStereoSample* output, unsigned nSamples); +unsigned GBAAudioResampleNN(struct GBAAudio*, float ratio, float* drift, struct GBAStereoSample* output, + unsigned nSamples); #endif struct GBASerializedState; diff --git a/src/gba/bios.c b/src/gba/bios.c index 1fc5a8cf0..3af31b9fd 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -175,7 +175,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) { void GBASwi16(struct ARMCore* cpu, int immediate) { struct GBA* gba = (struct GBA*) cpu->master; GBALog(gba, GBA_LOG_SWI, "SWI: %02X r0: %08X r1: %08X r2: %08X r3: %08X", - immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]); + immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]); if (gba->memory.fullBios) { ARMRaiseSWI(cpu); @@ -192,8 +192,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { GBAHalt(gba); break; case 0x05: - // VBlankIntrWait - // Fall through: + // VBlankIntrWait + // Fall through: case 0x04: // IntrWait ARMRaiseSWI(cpu); @@ -236,14 +236,14 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } switch (cpu->gprs[1] >> BASE_OFFSET) { - default: - GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 destination"); - // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: - _unLz77(gba, immediate == 0x11 ? 1 : 2); - break; + default: + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad LZ77 destination"); + // Fall through + case REGION_WORKING_RAM: + case REGION_WORKING_IRAM: + case REGION_VRAM: + _unLz77(gba, immediate == 0x11 ? 1 : 2); + break; } break; case 0x13: @@ -252,14 +252,14 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } switch (cpu->gprs[1] >> BASE_OFFSET) { - default: - GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman destination"); - // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: - _unHuffman(gba); - break; + default: + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad Huffman destination"); + // Fall through + case REGION_WORKING_RAM: + case REGION_WORKING_IRAM: + case REGION_VRAM: + _unHuffman(gba); + break; } break; case 0x14: @@ -269,14 +269,14 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } switch (cpu->gprs[1] >> BASE_OFFSET) { - default: - GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL destination"); - // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: - _unRl(gba, immediate == 0x14 ? 1 : 2); - break; + default: + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad RL destination"); + // Fall through + case REGION_WORKING_RAM: + case REGION_WORKING_IRAM: + case REGION_VRAM: + _unRl(gba, immediate == 0x14 ? 1 : 2); + break; } break; case 0x16: @@ -287,14 +287,14 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } switch (cpu->gprs[1] >> BASE_OFFSET) { - default: - GBALog(gba, GBA_LOG_GAME_ERROR, "Bad UnFilter destination"); - // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: - _unFilter(gba, immediate == 0x18 ? 2 : 1, immediate == 0x16 ? 1 : 2); - break; + default: + GBALog(gba, GBA_LOG_GAME_ERROR, "Bad UnFilter destination"); + // Fall through + case REGION_WORKING_RAM: + case REGION_WORKING_IRAM: + case REGION_VRAM: + _unFilter(gba, immediate == 0x18 ? 2 : 1, immediate == 0x16 ? 1 : 2); + break; } break; case 0x1F: @@ -457,7 +457,6 @@ static void _unHuffman(struct GBA* gba) { block = 0; } } - } cpu->gprs[0] = source; cpu->gprs[1] = dest; diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index e9bb8221d..0dd6f68b2 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -198,7 +198,7 @@ bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) { switch (set->gsaVersion) { case 0: GBACheatSetGameSharkVersion(set, 1); - // Fall through + // Fall through case 1: GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds); return GBACheatAddGameSharkRaw(set, o1, o2); diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 62693a2af..8ec3b67e4 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -298,7 +298,7 @@ bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t switch (set->gsaVersion) { case 0: GBACheatSetGameSharkVersion(set, 3); - // Fall through + // Fall through case 1: GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds); return GBACheatAddProActionReplayRaw(set, o1, o2); diff --git a/src/gba/gba.c b/src/gba/gba.c index 2bef0c6a0..1be44c506 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -91,17 +91,19 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { gba->idleDetectionStep = 0; gba->idleDetectionFailures = 0; - gba->realisticTiming = false; + gba->realisticTiming = true; gba->performingDMA = false; } void GBAUnloadROM(struct GBA* gba) { - if (gba->pristineRom == gba->memory.rom) { - gba->memory.rom = 0; - } else { - mappedMemoryFree(gba->pristineRom, gba->pristineRomSize); + if (gba->memory.rom && gba->pristineRom != gba->memory.rom) { + if (gba->yankedRomSize) { + gba->yankedRomSize = 0; + } + mappedMemoryFree(gba->memory.rom, SIZE_CART0); } + gba->memory.rom = 0; if (gba->romVf) { gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize); @@ -433,10 +435,10 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { size_t patchedSize = patch->outputSize(patch, gba->memory.romSize); - if (!patchedSize) { + if (!patchedSize || patchedSize > SIZE_CART0) { return; } - gba->memory.rom = anonymousMemoryMap(patchedSize); + gba->memory.rom = anonymousMemoryMap(SIZE_CART0); if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) { mappedMemoryFree(gba->memory.rom, patchedSize); gba->memory.rom = gba->pristineRom; @@ -449,7 +451,12 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { void GBATimerUpdateRegister(struct GBA* gba, int timer) { struct GBATimer* currentTimer = &gba->timers[timer]; if (currentTimer->enable && !currentTimer->countUp) { - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits); + int32_t prefetchSkew = 0; + if (gba->memory.lastPrefetchedPc - gba->memory.lastPrefetchedLoads * WORD_SIZE_THUMB >= (uint32_t) gba->cpu->gprs[ARM_PC]) { + prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB; + } + // Reading this takes two cycles (1N+1I), so let's remove them preemptively + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent - 2 + prefetchSkew) >> currentTimer->prescaleBits); } } diff --git a/src/gba/gba.h b/src/gba/gba.h index 25212d19e..aff2120d7 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -203,7 +203,8 @@ void GBAHalt(struct GBA* gba); void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger); void GBADetachDebugger(struct GBA* gba); -void GBASetBreakpoint(struct GBA* gba, struct ARMComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode); +void GBASetBreakpoint(struct GBA* gba, struct ARMComponent* component, uint32_t address, enum ExecutionMode mode, + uint32_t* opcode); void GBAClearBreakpoint(struct GBA* gba, uint32_t address, enum ExecutionMode mode, uint32_t opcode); void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname); @@ -220,7 +221,7 @@ void GBAGetGameTitle(struct GBA* gba, char* out); void GBAFrameStarted(struct GBA* gba); void GBAFrameEnded(struct GBA* gba); -ATTRIBUTE_FORMAT(printf,3,4) +ATTRIBUTE_FORMAT(printf, 3, 4) void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...); ATTRIBUTE_FORMAT(printf, 3, 4) diff --git a/src/gba/hardware.c b/src/gba/hardware.c index f2a6fef44..d764325fd 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -6,6 +6,11 @@ #include "hardware.h" #include "gba/serialize.h" +#include "util/hash.h" + +#ifdef PSP2 +#include +#endif static void _readPins(struct GBACartridgeHardware* hw); static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins); @@ -16,6 +21,8 @@ static void _rtcProcessByte(struct GBACartridgeHardware* hw); static void _rtcUpdateClock(struct GBACartridgeHardware* hw); static unsigned _rtcBCD(unsigned value); +static time_t _rtcGenericCallback(struct GBARTCSource* source); + static void _gyroReadPins(struct GBACartridgeHardware* hw); static void _rumbleReadPins(struct GBACartridgeHardware* hw); @@ -245,7 +252,9 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) { time_t t; struct GBARTCSource* rtc = hw->p->rtcSource; if (rtc) { - rtc->sample(rtc); + if (rtc->sample) { + rtc->sample(rtc); + } t = rtc->unixTime(rtc); } else { t = time(0); @@ -253,6 +262,10 @@ void _rtcUpdateClock(struct GBACartridgeHardware* hw) { struct tm date; #ifdef _WIN32 date = *localtime(&t); +#elif defined(PSP2) + SceRtcTime scertc; + sceRtcGetCurrentClockLocalTime(&scertc); + sceRtcGetTime_t(&scertc, &t); #else localtime_r(&t, &date); #endif @@ -276,6 +289,27 @@ unsigned _rtcBCD(unsigned value) { return counter; } +time_t _rtcGenericCallback(struct GBARTCSource* source) { + struct GBARTCGenericSource* rtc = (struct GBARTCGenericSource*) source; + switch (rtc->override) { + case RTC_NO_OVERRIDE: + default: + return time(0); + case RTC_FIXED: + return rtc->value; + case RTC_FAKE_EPOCH: + return rtc->value + rtc->p->video.frameCounter * (int64_t) VIDEO_TOTAL_LENGTH / GBA_ARM7TDMI_FREQUENCY; + } +} + +void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba) { + rtc->p = gba; + rtc->override = RTC_NO_OVERRIDE; + rtc->value = 0; + rtc->d.sample = 0; + rtc->d.unixTime = _rtcGenericCallback; +} + // == Gyro void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) { @@ -420,6 +454,29 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) { return 0xFF; } +// == Game Boy Player + +static const uint16_t _logoPalette[] = { + 0xFFDF, 0x640C, 0xE40C, 0xE42D, 0x644E, 0xE44E, 0xE46E, 0x68AF, + 0xE8B0, 0x68D0, 0x68F0, 0x6911, 0xE911, 0x6D32, 0xED32, 0xED73, + 0x6D93, 0xED94, 0x6DB4, 0xF1D5, 0x71F5, 0xF1F6, 0x7216, 0x7257, + 0xF657, 0x7678, 0xF678, 0xF699, 0xF6B9, 0x76D9, 0xF6DA, 0x7B1B, + 0xFB1B, 0xFB3C, 0x7B5C, 0x7B7D, 0xFF7D, 0x7F9D, 0x7FBE, 0x7FFF, + 0x642D, 0x648E, 0xE88F, 0xE8F1, 0x6D52, 0x6D73, 0xF1B4, 0xF216, + 0x7237, 0x7698, 0x7AFA, 0xFAFA, 0xFB5C, 0xFFBE, 0x7FDE, 0xFFFF, + 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static const uint32_t _logoHash = 0xEEDA6963; + +bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { + if (memcmp(video->palette, _logoPalette, sizeof(_logoPalette)) != 0) { + return false; + } + uint32_t hash = hash32(&video->renderer->vram[0x4000], 0x4000, 0); + return hash == _logoHash; +} + // == Serialization void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) { @@ -442,7 +499,6 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer hw->readWrite = state->hw.readWrite; hw->pinState = state->hw.pinState; hw->direction = state->hw.pinDirection; - // TODO: Deterministic RTC hw->rtc = state->hw.rtc; hw->gyroSample = state->hw.gyroSample; hw->gyroEdge = state->hw.gyroEdge; diff --git a/src/gba/hardware.h b/src/gba/hardware.h index d7c9a6dfc..5ba17c221 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -35,6 +35,17 @@ struct GBARTCSource { time_t (*unixTime)(struct GBARTCSource*); }; +struct GBARTCGenericSource { + struct GBARTCSource d; + struct GBA* p; + enum { + RTC_NO_OVERRIDE, + RTC_FIXED, + RTC_FAKE_EPOCH + } override; + int64_t value; +}; + enum GBAHardwareDevice { HW_NO_OVERRIDE = 0x8000, HW_NONE = 0, @@ -129,6 +140,11 @@ void GBAHardwareGPIOWrite(struct GBACartridgeHardware* gpio, uint32_t address, u void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, uint8_t value); uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address); +struct GBAVideo; +bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video); + +void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba); + struct GBASerializedState; void GBAHardwareSerialize(const struct GBACartridgeHardware* gpio, struct GBASerializedState* state); void GBAHardwareDeserialize(struct GBACartridgeHardware* gpio, const struct GBASerializedState* state); diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 6f9ed4d78..8402fb3b1 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -3,49 +3,49 @@ #include "gba/memory.h" const uint8_t hleBios[SIZE_BIOS] = { - 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x07, 0x00, 0x00, 0xea, - 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x28, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x5d, 0xe3, - 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, - 0x02, 0xb0, 0x5e, 0xe5, 0x8c, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7, - 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9, - 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, 0x0c, 0xf0, 0x29, 0xe1, - 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11, - 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8, - 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe8, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, - 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, - 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, - 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5, 0x01, 0x00, 0xa0, 0xe3, - 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3, - 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, - 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1, - 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, - 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, - 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a, - 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1, - 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, - 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8, - 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, - 0x16, 0x00, 0x00, 0xea, 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, - 0xa3, 0x35, 0x81, 0xe0, 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, - 0xb2, 0x20, 0xc1, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, - 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, - 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, - 0xfb, 0xff, 0xff, 0xba, 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, - 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, - 0xb2, 0x20, 0xd0, 0xb0, 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, - 0x00, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, - 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, - 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, - 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, - 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, - 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, - 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, - 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8 + 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x07, 0x00, 0x00, 0xea, + 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, + 0x28, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x5d, 0xe3, + 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, + 0x02, 0xb0, 0x5e, 0xe5, 0x8c, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7, + 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9, + 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, 0x0c, 0xf0, 0x29, 0xe1, + 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11, + 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8, + 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe8, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, + 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, + 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, + 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5, 0x01, 0x00, 0xa0, 0xe3, + 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3, + 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, + 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1, + 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, + 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, + 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a, + 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1, + 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, + 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8, + 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, + 0x16, 0x00, 0x00, 0xea, 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, + 0xa3, 0x35, 0x81, 0xe0, 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, + 0xb2, 0x20, 0xc1, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, + 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, + 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, + 0xfb, 0xff, 0xff, 0xba, 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, + 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, + 0xb2, 0x20, 0xd0, 0xb0, 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, + 0x00, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, + 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, + 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, + 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, + 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, + 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, + 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, + 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8 }; diff --git a/src/gba/input.h b/src/gba/input.h index 91af955d0..8610b6f24 100644 --- a/src/gba/input.h +++ b/src/gba/input.h @@ -51,7 +51,9 @@ void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Config const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId); void GBAInputSetPreferredDevice(struct Configuration*, uint32_t type, int playerId, const char* deviceName); -const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, const char* profile); -void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, const char* profile); +const char* GBAInputGetCustomValue(const struct Configuration* config, uint32_t type, const char* key, + const char* profile); +void GBAInputSetCustomValue(struct Configuration* config, uint32_t type, const char* key, const char* value, + const char* profile); #endif diff --git a/src/gba/io.c b/src/gba/io.c index c9957f301..a93227669 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -333,7 +333,7 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { case REG_SOUND3CNT_X: GBAAudioWriteSOUND3CNT_X(&gba->audio, value); // TODO: The low bits need to not be readable, but still 8-bit writable - value &= 0x43FF; + value &= 0x47FF; break; case REG_SOUND4CNT_LO: GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); diff --git a/src/gba/memory.c b/src/gba/memory.c index be843b55e..32fa3f917 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -22,6 +22,7 @@ static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both AR static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); static void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info); +static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait); static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 }; static const char GBA_BASE_WAITSTATES_32[16] = { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 9 }; @@ -41,6 +42,7 @@ void GBAMemoryInit(struct GBA* gba) { cpu->memory.store16 = GBAStore16; cpu->memory.store8 = GBAStore8; cpu->memory.storeMultiple = GBAStoreMultiple; + cpu->memory.stall = GBAMemoryStall; gba->memory.bios = (uint32_t*) hleBios; gba->memory.fullBios = 0; @@ -76,8 +78,6 @@ void GBAMemoryInit(struct GBA* gba) { cpu->memory.activeSeqCycles16 = 0; cpu->memory.activeNonseqCycles32 = 0; cpu->memory.activeNonseqCycles16 = 0; - cpu->memory.activeUncachedCycles32 = 0; - cpu->memory.activeUncachedCycles16 = 0; gba->memory.biosPrefetch = 0; } @@ -113,6 +113,9 @@ void GBAMemoryReset(struct GBA* gba) { gba->memory.nextDMA = INT_MAX; gba->memory.eventDiff = 0; + gba->memory.prefetch = false; + gba->memory.lastPrefetchedPc = 0; + if (!gba->memory.wram || !gba->memory.iwram) { GBAMemoryDeinit(gba); GBALog(gba, GBA_LOG_FATAL, "Could not map memory"); @@ -232,6 +235,8 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } gba->lastJump = address; + memory->lastPrefetchedPc = 0; + memory->lastPrefetchedLoads = 0; if (newRegion == memory->activeRegion && (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize)) { return; } @@ -268,7 +273,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { if ((address & (SIZE_CART0 - 1)) < memory->romSize) { break; } - // Fall through + // Fall through default: memory->activeRegion = -1; cpu->memory.activeRegion = _deadbeef; @@ -278,12 +283,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } return; } - cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[newRegion]; - cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[newRegion]; - cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[newRegion]; - cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[newRegion]; - cpu->memory.activeUncachedCycles32 = memory->waitstatesNonseq32[newRegion]; - cpu->memory.activeUncachedCycles16 = memory->waitstatesNonseq16[newRegion]; + cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; + cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; } #define LOAD_BAD \ @@ -412,7 +415,11 @@ uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } if (cycleCounter) { - *cycleCounter += 1 + wait; + wait += 2; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } // Unaligned 32-bit loads are "rotated" so they make some semblance of sense int rotate = (address & 3) << 3; @@ -474,7 +481,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); } else { GBALog(gba, GBA_LOG_GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); - value = (address >> 1) & 0xFFFF; \ + value = (address >> 1) & 0xFFFF; } break; case REGION_CART2_EX: @@ -485,7 +492,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); } else { GBALog(gba, GBA_LOG_GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); - value = (address >> 1) & 0xFFFF; \ + value = (address >> 1) & 0xFFFF; } break; case REGION_CART_SRAM: @@ -503,7 +510,11 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } if (cycleCounter) { - *cycleCounter += 1 + wait; + wait += 2; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } // Unaligned 16-bit loads are "unpredictable", but the GBA rotates them, so we have to, too. int rotate = (address & 1) << 3; @@ -565,7 +576,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = ((uint8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; } else { GBALog(gba, GBA_LOG_GAME_ERROR, "Out of bounds ROM Load8: 0x%08X", address); - value = (address >> 1) & 0xFF; \ + value = (address >> 1) & 0xFF; } break; case REGION_CART_SRAM: @@ -595,7 +606,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } if (cycleCounter) { - *cycleCounter += 1 + wait; + wait += 2; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } return value; } @@ -682,7 +697,11 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle } if (cycleCounter) { - *cycleCounter += 1 + wait; + ++wait; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } } @@ -742,7 +761,11 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } if (cycleCounter) { - *cycleCounter += 1 + wait; + ++wait; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } } @@ -808,7 +831,11 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo } if (cycleCounter) { - *cycleCounter += 1 + wait; + ++wait; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } + *cycleCounter += wait; } } @@ -1100,6 +1127,10 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L } if (cycleCounter) { + ++wait; + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } *cycleCounter += wait; } @@ -1206,6 +1237,9 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum } if (cycleCounter) { + if (address >> BASE_OFFSET < REGION_CART0) { + wait = GBAMemoryStall(cpu, wait); + } *cycleCounter += wait; } @@ -1232,7 +1266,7 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { int ws2seq = (parameters & 0x0400) >> 10; int prefetch = parameters & 0x4000; - memory->waitstatesNonseq16[REGION_CART_SRAM] = memory->waitstatesNonseq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesNonseq16[REGION_CART_SRAM] = memory->waitstatesNonseq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; memory->waitstatesSeq16[REGION_CART_SRAM] = memory->waitstatesSeq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; memory->waitstatesNonseq32[REGION_CART_SRAM] = memory->waitstatesNonseq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; memory->waitstatesSeq32[REGION_CART_SRAM] = memory->waitstatesSeq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; @@ -1253,50 +1287,13 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { memory->waitstatesSeq32[REGION_CART1] = memory->waitstatesSeq32[REGION_CART1_EX] = 2 * memory->waitstatesSeq16[REGION_CART1] + 1; memory->waitstatesSeq32[REGION_CART2] = memory->waitstatesSeq32[REGION_CART2_EX] = 2 * memory->waitstatesSeq16[REGION_CART2] + 1; - if (!prefetch) { - memory->waitstatesPrefetchSeq16[REGION_CART0] = memory->waitstatesPrefetchSeq16[REGION_CART0_EX] = memory->waitstatesSeq16[REGION_CART0]; - memory->waitstatesPrefetchSeq16[REGION_CART1] = memory->waitstatesPrefetchSeq16[REGION_CART1_EX] = memory->waitstatesSeq16[REGION_CART1]; - memory->waitstatesPrefetchSeq16[REGION_CART2] = memory->waitstatesPrefetchSeq16[REGION_CART2_EX] = memory->waitstatesSeq16[REGION_CART2]; + memory->prefetch = prefetch; - memory->waitstatesPrefetchSeq32[REGION_CART0] = memory->waitstatesPrefetchSeq32[REGION_CART0_EX] = memory->waitstatesSeq32[REGION_CART0]; - memory->waitstatesPrefetchSeq32[REGION_CART1] = memory->waitstatesPrefetchSeq32[REGION_CART1_EX] = memory->waitstatesSeq32[REGION_CART1]; - memory->waitstatesPrefetchSeq32[REGION_CART2] = memory->waitstatesPrefetchSeq32[REGION_CART2_EX] = memory->waitstatesSeq32[REGION_CART2]; + cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; + cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; - memory->waitstatesPrefetchNonseq16[REGION_CART0] = memory->waitstatesPrefetchNonseq16[REGION_CART0_EX] = memory->waitstatesNonseq16[REGION_CART0]; - memory->waitstatesPrefetchNonseq16[REGION_CART1] = memory->waitstatesPrefetchNonseq16[REGION_CART1_EX] = memory->waitstatesNonseq16[REGION_CART1]; - memory->waitstatesPrefetchNonseq16[REGION_CART2] = memory->waitstatesPrefetchNonseq16[REGION_CART2_EX] = memory->waitstatesNonseq16[REGION_CART2]; - - memory->waitstatesPrefetchNonseq32[REGION_CART0] = memory->waitstatesPrefetchNonseq32[REGION_CART0_EX] = memory->waitstatesNonseq32[REGION_CART0]; - memory->waitstatesPrefetchNonseq32[REGION_CART1] = memory->waitstatesPrefetchNonseq32[REGION_CART1_EX] = memory->waitstatesNonseq32[REGION_CART1]; - memory->waitstatesPrefetchNonseq32[REGION_CART2] = memory->waitstatesPrefetchNonseq32[REGION_CART2_EX] = memory->waitstatesNonseq32[REGION_CART2]; - } else { - // Assume it stalls one cycle to pull a value from the prefetch - // This needs more research to tell if it's accurate or not - memory->waitstatesPrefetchSeq16[REGION_CART0] = memory->waitstatesPrefetchSeq16[REGION_CART0_EX] = 1; - memory->waitstatesPrefetchSeq16[REGION_CART1] = memory->waitstatesPrefetchSeq16[REGION_CART1_EX] = 1; - memory->waitstatesPrefetchSeq16[REGION_CART2] = memory->waitstatesPrefetchSeq16[REGION_CART2_EX] = 1; - - memory->waitstatesPrefetchSeq32[REGION_CART0] = memory->waitstatesPrefetchSeq32[REGION_CART0_EX] = 2; - memory->waitstatesPrefetchSeq32[REGION_CART1] = memory->waitstatesPrefetchSeq32[REGION_CART1_EX] = 2; - memory->waitstatesPrefetchSeq32[REGION_CART2] = memory->waitstatesPrefetchSeq32[REGION_CART2_EX] = 2; - - memory->waitstatesPrefetchNonseq16[REGION_CART0] = memory->waitstatesPrefetchNonseq16[REGION_CART0_EX] = 1; - memory->waitstatesPrefetchNonseq16[REGION_CART1] = memory->waitstatesPrefetchNonseq16[REGION_CART1_EX] = 1; - memory->waitstatesPrefetchNonseq16[REGION_CART2] = memory->waitstatesPrefetchNonseq16[REGION_CART2_EX] = 1; - - memory->waitstatesPrefetchNonseq32[REGION_CART0] = memory->waitstatesPrefetchNonseq32[REGION_CART0_EX] = 2; - memory->waitstatesPrefetchNonseq32[REGION_CART1] = memory->waitstatesPrefetchNonseq32[REGION_CART1_EX] = 2; - memory->waitstatesPrefetchNonseq32[REGION_CART2] = memory->waitstatesPrefetchNonseq32[REGION_CART2_EX] = 2; - } - - cpu->memory.activeSeqCycles32 = memory->waitstatesPrefetchSeq32[memory->activeRegion]; - cpu->memory.activeSeqCycles16 = memory->waitstatesPrefetchSeq16[memory->activeRegion]; - - cpu->memory.activeNonseqCycles32 = memory->waitstatesPrefetchNonseq32[memory->activeRegion]; - cpu->memory.activeNonseqCycles16 = memory->waitstatesPrefetchNonseq16[memory->activeRegion]; - - cpu->memory.activeUncachedCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; - cpu->memory.activeUncachedCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; + cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; + cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; } void GBAMemoryWriteDMASAD(struct GBA* gba, int dma, uint32_t address) { @@ -1528,6 +1525,52 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) { cpu->cycles += cycles; } +int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { + struct GBA* gba = (struct GBA*) cpu->master; + struct GBAMemory* memory = &gba->memory; + + if (memory->activeRegion < REGION_CART0 || !memory->prefetch) { + // The wait is the stall + return wait; + } + + int32_t s = cpu->memory.activeSeqCycles16 + 1; + int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; + + // Figure out how many sequential loads we can jam in + int32_t stall = s; + int32_t loads = 1; + int32_t previousLoads = 0; + + // Don't prefetch too much if we're overlapping with a previous prefetch + uint32_t dist = (memory->lastPrefetchedPc - cpu->gprs[ARM_PC]) >> 1; + if (dist < memory->lastPrefetchedLoads) { + previousLoads = dist; + } + while (stall < wait) { + stall += s; + ++loads; + } + if (loads + previousLoads > 8) { + int diff = (loads + previousLoads) - 8; + loads -= diff; + stall -= s * diff; + } else if (stall > wait && loads == 1) { + // We might need to stall a bit extra if we haven't finished the first S cycle + wait = stall; + } + // This instruction used to have an N, convert it to an S. + wait -= n2s; + + // TODO: Invalidate prefetch on branch + memory->lastPrefetchedLoads = loads; + memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * loads; + + // The next |loads|S waitstates disappear entirely, so long as they're all in a row + cpu->cycles -= (s - 1) * loads; + return wait; +} + void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) { memcpy(state->wram, memory->wram, SIZE_WORKING_RAM); memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM); diff --git a/src/gba/memory.h b/src/gba/memory.h index 053e71f20..dd9d4066b 100644 --- a/src/gba/memory.h +++ b/src/gba/memory.h @@ -86,7 +86,6 @@ enum DMATiming { DMA_TIMING_CUSTOM = 3 }; - DECL_BITFIELD(GBADMARegister, uint16_t); DECL_BITS(GBADMARegister, DestControl, 5, 2); DECL_BITS(GBADMARegister, SrcControl, 7, 2); @@ -131,6 +130,9 @@ struct GBAMemory { char waitstatesPrefetchNonseq32[16]; char waitstatesPrefetchNonseq16[16]; int activeRegion; + bool prefetch; + uint32_t lastPrefetchedPc; + uint32_t lastPrefetchedLoads; uint32_t biosPrefetch; struct GBADMA dma[4]; @@ -156,8 +158,10 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* old); void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old); -uint32_t GBALoadMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); -uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, int* cycleCounter); +uint32_t GBALoadMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); +uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction, + int* cycleCounter); void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index f63d7951e..6d5d7e93e 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -14,11 +14,16 @@ #define VIDEO_CHECKS true #endif -void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); -void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); -void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); -void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); -void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); +void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, + struct GBAVideoSoftwareBackground* background, int y); +void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, + struct GBAVideoSoftwareBackground* background, int y); +void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, + struct GBAVideoSoftwareBackground* background, int y); +void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, + struct GBAVideoSoftwareBackground* background, int y); +void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, + struct GBAVideoSoftwareBackground* background, int y); int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y); void GBAVideoSoftwareRendererPostprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority); @@ -58,22 +63,24 @@ static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* rend *pixel = color; } -static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) { +static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, + uint32_t current) { UNUSED(renderer); if (color < current) { *pixel = color | (current & FLAG_OBJWIN); } } -static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) { +static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, + uint32_t current) { UNUSED(renderer); if (color < current) { *pixel = color; } } -#define COMPOSITE_16_OBJWIN(BLEND) \ - if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \ +#define COMPOSITE_16_OBJWIN(BLEND) \ + if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \ unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \ unsigned mergedFlags = flags; \ if (current & FLAG_OBJWIN) { \ @@ -115,78 +122,86 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re tileData >>= 8; // TODO: Remove UNUSEDs after implementing OBJWIN for modes 3 - 5 -#define PREPARE_OBJWIN \ - int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \ - int objwinOnly = 0; \ - int objwinForceEnable = 0; \ - UNUSED(objwinForceEnable); \ - color_t* objwinPalette = renderer->normalPalette; \ - UNUSED(objwinPalette); \ - if (objwinSlowPath) { \ - if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \ - objwinPalette = renderer->variantPalette; \ - } \ - switch (background->index) { \ - case 0: \ - objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \ - objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \ - break; \ - case 1: \ - objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \ - objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \ - break; \ - case 2: \ - objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \ - objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \ - break; \ - case 3: \ - objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \ - objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \ - break; \ - } \ +#define PREPARE_OBJWIN \ + int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \ + int objwinOnly = 0; \ + int objwinForceEnable = 0; \ + UNUSED(objwinForceEnable); \ + color_t* objwinPalette = renderer->normalPalette; \ + UNUSED(objwinPalette); \ + if (objwinSlowPath) { \ + if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && \ + (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \ + objwinPalette = renderer->variantPalette; \ + } \ + switch (background->index) { \ + case 0: \ + objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && \ + GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \ + break; \ + case 1: \ + objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && \ + GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \ + break; \ + case 2: \ + objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && \ + GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \ + break; \ + case 3: \ + objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && \ + GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \ + objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \ + break; \ + } \ } -#define BACKGROUND_BITMAP_INIT \ - int32_t x = background->sx + (renderer->start - 1) * background->dx; \ - int32_t y = background->sy + (renderer->start - 1) * background->dy; \ - int mosaicH = 0; \ - int mosaicWait = 0; \ - if (background->mosaic) { \ - int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \ - y -= (inY % mosaicV) * background->dmy; \ - x -= (inY % mosaicV) * background->dmx; \ - mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \ - mosaicWait = renderer->start % (mosaicH + 1); \ - } \ - int32_t localX; \ - int32_t localY; \ - \ +#define BACKGROUND_BITMAP_INIT \ + int32_t x = background->sx + (renderer->start - 1) * background->dx; \ + int32_t y = background->sy + (renderer->start - 1) * background->dy; \ + int mosaicH = 0; \ + int mosaicWait = 0; \ + if (background->mosaic) { \ + int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \ + y -= (inY % mosaicV) * background->dmy; \ + x -= (inY % mosaicV) * background->dmx; \ + mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \ + mosaicWait = renderer->start % (mosaicH + 1); \ + } \ + int32_t localX; \ + int32_t localY; \ + \ int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \ - flags |= FLAG_TARGET_2 * background->target2; \ - int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \ - objwinFlags |= flags; \ - flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \ - if (renderer->blda == 0x10 && renderer->bldb == 0) { \ - flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ - objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ - } \ - int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \ - color_t* palette = renderer->normalPalette; \ - if (variant) { \ - palette = renderer->variantPalette; \ - } \ - UNUSED(palette); \ + flags |= FLAG_TARGET_2 * background->target2; \ + int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \ + GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \ + objwinFlags |= flags; \ + flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \ + GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \ + if (renderer->blda == 0x10 && renderer->bldb == 0) { \ + flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ + objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ + } \ + int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && \ + (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \ + color_t* palette = renderer->normalPalette; \ + if (variant) { \ + palette = renderer->variantPalette; \ + } \ + UNUSED(palette); \ PREPARE_OBJWIN; -#define BACKGROUND_BITMAP_ITERATE(W, H) \ - x += background->dx; \ - y += background->dy; \ - \ +#define BACKGROUND_BITMAP_ITERATE(W, H) \ + x += background->dx; \ + y += background->dy; \ + \ if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \ - continue; \ - } else { \ - localX = x; \ - localY = y; \ + continue; \ + } else { \ + localX = x; \ + localY = y; \ } static inline unsigned _brighten(unsigned color, int y) { diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 4db0c8588..b313c1472 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -103,11 +103,16 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio eventDiff is negative"); error = true; } - if (state->audio.ch1.envelopeNextStep < 0 || state->audio.ch1.waveNextStep < 0 || state->audio.ch1.sweepNextStep < 0 || state->audio.ch1.nextEvent < 0) { + if (!state->audio.ch1Dead && (state->audio.ch1.envelopeNextStep < 0 || + state->audio.ch1.waveNextStep < 0 || + state->audio.ch1.sweepNextStep < 0 || + state->audio.ch1.nextEvent < 0)) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio channel 1 register is negative"); error = true; } - if (state->audio.ch2.envelopeNextStep < 0 || state->audio.ch2.waveNextStep < 0 || state->audio.ch2.nextEvent < 0) { + if (!state->audio.ch2Dead && (state->audio.ch2.envelopeNextStep < 0 || + state->audio.ch2.waveNextStep < 0 || + state->audio.ch2.nextEvent < 0)) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio channel 2 register is negative"); error = true; } @@ -115,7 +120,8 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio channel 3 register is negative"); error = true; } - if (state->audio.ch4.envelopeNextStep < 0 || state->audio.ch4.nextEvent < 0) { + if (!state->audio.ch4Dead && (state->audio.ch4.envelopeNextStep < 0 || + state->audio.ch4.nextEvent < 0)) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio channel 4 register is negative"); error = true; } @@ -225,7 +231,10 @@ static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) { struct GBASerializedState state; uLongf len = sizeof(state); uncompress((Bytef*) &state, &len, chunk->data, chunk->size); - return GBADeserialize(png_get_user_chunk_ptr(png), &state); + if (!GBADeserialize(png_get_user_chunk_ptr(png), &state)) { + longjmp(png_jmpbuf(png), 1); + } + return 1; } static bool _loadPNGState(struct GBA* gba, struct VFile* vf) { @@ -239,15 +248,17 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) { uint32_t* pixels = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4); PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs"); - PNGReadHeader(png, info); - PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); - PNGReadFooter(png, end); + bool success = PNGReadHeader(png, info); + success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); + success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); - gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels); - GBASyncForceFrame(gba->sync); + if (success) { + gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels); + GBASyncForceFrame(gba->sync); + } free(pixels); - return true; + return success; } #endif @@ -295,20 +306,20 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot) { vf->unmap(vf, state, sizeof(struct GBASerializedState)); return true; } - #ifdef USE_PNG +#ifdef USE_PNG else { return _savePNGState(gba, vf); } - #endif +#endif return false; } bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) { - #ifdef USE_PNG +#ifdef USE_PNG if (isPNG(vf)) { return _loadPNGState(gba, vf); } - #endif +#endif if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) { return false; } diff --git a/src/gba/sharkport.c b/src/gba/sharkport.c index 88f21f721..5284e57b1 100644 --- a/src/gba/sharkport.c +++ b/src/gba/sharkport.c @@ -123,7 +123,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec if (copySize > SIZE_CART_FLASH512) { GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming); } - // Fall through + // Fall through case SAVEDATA_FLASH1M: if (copySize > SIZE_CART_FLASH1M) { copySize = SIZE_CART_FLASH1M; @@ -149,7 +149,6 @@ cleanup: return false; } - bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) { union { char c[0x1C]; diff --git a/src/gba/supervisor/cli.c b/src/gba/supervisor/cli.c index 3b2eb159f..06e9d20f5 100644 --- a/src/gba/supervisor/cli.c +++ b/src/gba/supervisor/cli.c @@ -30,11 +30,9 @@ struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = { { "save", _save, CLIDVParse, "Save a savestate" }, { 0, 0, 0, 0 } }; -#endif struct GBACLIDebugger* GBACLIDebuggerCreate(struct GBAThread* context) { struct GBACLIDebugger* debugger = malloc(sizeof(struct GBACLIDebugger)); -#ifdef USE_CLI_DEBUGGER debugger->d.init = _GBACLIDebuggerInit; debugger->d.deinit = _GBACLIDebuggerDeinit; debugger->d.custom = _GBACLIDebuggerCustom; @@ -44,14 +42,10 @@ struct GBACLIDebugger* GBACLIDebuggerCreate(struct GBAThread* context) { debugger->d.commands = _GBACLIDebuggerCommands; debugger->context = context; -#else - UNUSED(context); -#endif return debugger; } -#ifdef USE_CLI_DEBUGGER static void _GBACLIDebuggerInit(struct CLIDebuggerSystem* debugger) { struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger; diff --git a/src/gba/supervisor/cli.h b/src/gba/supervisor/cli.h index 30cd90b0c..17dadf273 100644 --- a/src/gba/supervisor/cli.h +++ b/src/gba/supervisor/cli.h @@ -6,21 +6,21 @@ #ifndef GBA_CLI_H #define GBA_CLI_H +#ifdef USE_CLI_DEBUGGER #include "debugger/cli-debugger.h" struct GBAThread; struct GBACLIDebugger { -#ifdef USE_CLI_DEBUGGER struct CLIDebuggerSystem d; struct GBAThread* context; bool frameAdvance; bool inVblank; -#endif }; struct GBACLIDebugger* GBACLIDebuggerCreate(struct GBAThread*); +#endif #endif diff --git a/src/gba/supervisor/config.c b/src/gba/supervisor/config.c index 4c50653f8..c32d72e42 100644 --- a/src/gba/supervisor/config.c +++ b/src/gba/supervisor/config.c @@ -13,6 +13,7 @@ #ifdef _WIN32 #include +#include #include #include #endif @@ -126,17 +127,42 @@ bool GBAConfigSave(const struct GBAConfig* config) { } void GBAConfigDirectory(char* out, size_t outLength) { + struct VFile* portable; #ifndef _WIN32 + getcwd(out, outLength); + strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); + portable = VFileOpen(out, O_RDONLY); + if (portable) { + getcwd(out, outLength); + portable->close(portable); + return; + } + char* home = getenv("HOME"); snprintf(out, outLength, "%s/.config", home); mkdir(out, 0755); snprintf(out, outLength, "%s/.config/%s", home, binaryName); mkdir(out, 0755); #else - char home[MAX_PATH]; - SHGetFolderPath(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, home); - snprintf(out, outLength, "%s\\%s", home, projectName); - CreateDirectoryA(out, NULL); + wchar_t wpath[MAX_PATH]; + wchar_t wprojectName[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH); + HMODULE hModule = GetModuleHandleW(NULL); + GetModuleFileNameW(hModule, wpath, MAX_PATH); + PathRemoveFileSpecW(wpath); + WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); + StringCchCatA(out, outLength, "\\portable.ini"); + portable = VFileOpen(out, O_RDONLY); + if (portable) { + portable->close(portable); + } else { + wchar_t* home; + SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &home); + StringCchPrintfW(wpath, MAX_PATH, L"%ws\\%ws", home, wprojectName); + CoTaskMemFree(home); + CreateDirectoryW(wpath, NULL); + } + WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); #endif } diff --git a/src/gba/supervisor/overrides.c b/src/gba/supervisor/overrides.c index ca57a9a76..5ed596644 100644 --- a/src/gba/supervisor/overrides.c +++ b/src/gba/supervisor/overrides.c @@ -8,7 +8,7 @@ #include "gba/gba.h" #include "gba/hardware.h" - #include "util/configuration.h" +#include "util/configuration.h" static const struct GBACartridgeOverride _overrides[] = { // Advance Wars diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index 8a39da3bb..587c6c9a3 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -108,7 +108,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { struct Patch patch; struct GBACheatDevice cheatDevice; struct GBAThread* threadContext = context; - struct ARMComponent* components[GBA_COMPONENT_MAX] = {0}; + struct ARMComponent* components[GBA_COMPONENT_MAX] = { 0 }; struct GBARRContext* movie = 0; int numComponents = GBA_COMPONENT_MAX; @@ -701,7 +701,7 @@ void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) { } GBAThreadLoadROM(threadContext, fname); - if(threadContext->gameDir) { + if (threadContext->gameDir) { _loadGameDir(threadContext); } diff --git a/src/gba/video.c b/src/gba/video.c index 8cf07ce27..66ebedd34 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -253,7 +253,6 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, un // Nothing to do } - void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) { memcpy(state->vram, video->renderer->vram, SIZE_VRAM); memcpy(state->oam, video->oam.raw, SIZE_OAM); diff --git a/src/gba/video.h b/src/gba/video.h index 086682247..a94235238 100644 --- a/src/gba/video.h +++ b/src/gba/video.h @@ -26,19 +26,17 @@ #define GBA_B8(X) (((X) >> 7) & 0xF8) enum { - VIDEO_CYCLES_PER_PIXEL = 4, - VIDEO_HORIZONTAL_PIXELS = 240, VIDEO_HBLANK_PIXELS = 68, VIDEO_HDRAW_LENGTH = 1006, VIDEO_HBLANK_LENGTH = 226, - VIDEO_HORIZONTAL_LENGTH = 1232, + VIDEO_HORIZONTAL_LENGTH = VIDEO_HDRAW_LENGTH + VIDEO_HBLANK_LENGTH, VIDEO_VERTICAL_PIXELS = 160, VIDEO_VBLANK_PIXELS = 68, - VIDEO_VERTICAL_TOTAL_PIXELS = 228, + VIDEO_VERTICAL_TOTAL_PIXELS = VIDEO_VERTICAL_PIXELS + VIDEO_VBLANK_PIXELS, - VIDEO_TOTAL_LENGTH = 280896, + VIDEO_TOTAL_LENGTH = VIDEO_HORIZONTAL_LENGTH * VIDEO_VERTICAL_TOTAL_PIXELS, REG_DISPSTAT_MASK = 0xFF38, @@ -67,7 +65,6 @@ DECL_BIT(GBAObjAttributesA, Mosaic, 12); DECL_BIT(GBAObjAttributesA, 256Color, 13); DECL_BITS(GBAObjAttributesA, Shape, 14, 2); - DECL_BITFIELD(GBAObjAttributesB, uint16_t); DECL_BITS(GBAObjAttributesB, X, 0, 9); DECL_BITS(GBAObjAttributesB, MatIndex, 9, 5); diff --git a/src/platform/commandline.h b/src/platform/commandline.h index c789e70ac..32275633b 100644 --- a/src/platform/commandline.h +++ b/src/platform/commandline.h @@ -46,7 +46,8 @@ struct GraphicsOpts { struct GBAThread; -bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser); +bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, + struct SubParser* subparser); void freeArguments(struct GBAArguments* opts); void usage(const char* arg0, const char* extraOptions); diff --git a/src/platform/ffmpeg/ffmpeg-encoder.c b/src/platform/ffmpeg/ffmpeg-encoder.c index 22c7381f8..a30f27e56 100644 --- a/src/platform/ffmpeg/ffmpeg-encoder.c +++ b/src/platform/ffmpeg/ffmpeg-encoder.c @@ -248,9 +248,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0); if (encoder->audio->codec->id == AV_CODEC_ID_AAC && - (strcasecmp(encoder->containerFormat, "mp4") || - strcasecmp(encoder->containerFormat, "m4v") || - strcasecmp(encoder->containerFormat, "mov"))) { + (strcasecmp(encoder->containerFormat, "mp4") || + strcasecmp(encoder->containerFormat, "m4v") || + strcasecmp(encoder->containerFormat, "mov"))) { // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out encoder->absf = av_bitstream_filter_init("aac_adtstoasc"); } @@ -292,19 +292,19 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->scaleContext = sws_getContext(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 - AV_PIX_FMT_RGB565, + AV_PIX_FMT_RGB565, #else - AV_PIX_FMT_BGR555, + AV_PIX_FMT_BGR555, #endif #else #ifndef USE_LIBAV - AV_PIX_FMT_0BGR32, + AV_PIX_FMT_0BGR32, #else - AV_PIX_FMT_BGR32, + AV_PIX_FMT_BGR32, #endif #endif - encoder->videoFrame->width, encoder->videoFrame->height, encoder->video->pix_fmt, - SWS_POINT, 0, 0, 0); + encoder->videoFrame->width, encoder->videoFrame->height, encoder->video->pix_fmt, + SWS_POINT, 0, 0, 0); av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32); avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE); @@ -378,8 +378,8 @@ void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t rig int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt); avresample_convert(encoder->resampleContext, - 0, 0, 0, - (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); + 0, 0, 0, + (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) { return; } @@ -402,8 +402,8 @@ void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int16_t left, int16_t rig if (encoder->absf) { AVPacket tempPacket = packet; int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0, - &tempPacket.data, &tempPacket.size, - packet.data, packet.size, 0); + &tempPacket.data, &tempPacket.size, + packet.data, packet.size, 0); if (success > 0) { #if LIBAVUTIL_VERSION_MAJOR >= 53 tempPacket.buf = av_buffer_create(tempPacket.data, tempPacket.size, av_buffer_default_free, 0, 0); diff --git a/src/platform/imagemagick/imagemagick-gif-encoder.c b/src/platform/imagemagick/imagemagick-gif-encoder.c index ef1f741c7..bd7b1d9e7 100644 --- a/src/platform/imagemagick/imagemagick-gif-encoder.c +++ b/src/platform/imagemagick/imagemagick-gif-encoder.c @@ -58,7 +58,7 @@ static void _magickPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRen renderer->getPixels(renderer, &stride, (void**) &pixels); size_t row; for (row = 0; row < VIDEO_VERTICAL_PIXELS; ++row) { - memcpy(&encoder->frame[row * VIDEO_HORIZONTAL_PIXELS], &pixels[row * 4 *stride], VIDEO_HORIZONTAL_PIXELS * 4); + memcpy(&encoder->frame[row * VIDEO_HORIZONTAL_PIXELS], &pixels[row * 4 * stride], VIDEO_HORIZONTAL_PIXELS * 4); } MagickConstituteImage(encoder->wand, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, "RGBP", CharPixel, encoder->frame); diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index 3d44c3da8..5d31b89ae 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -78,7 +78,7 @@ void GBAGLContextDrawFrame(struct VideoBackend* v) { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_INT, 0, _glVertices); glTexCoordPointer(2, GL_INT, 0, _glTexCoords); - glMatrixMode (GL_PROJECTION); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, 0, 1); glMatrixMode(GL_MODELVIEW); diff --git a/src/platform/psp2/CMakeLists.txt b/src/platform/psp2/CMakeLists.txt new file mode 100644 index 000000000..1736db4ba --- /dev/null +++ b/src/platform/psp2/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/psp2/*.c) + +set(PLATFORM_LIBRARY -lvita2d -lSceCtrl_stub -lSceRtc_stub -lSceGxm_stub -lSceDisplay_stub -lm_stub) + +add_executable(${BINARY_NAME}.elf ${PLATFORM_SRC}) +target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${PLATFORM_LIBRARY}) +set_target_properties(${BINARY_NAME}.elf PROPERTIES OUTPUT_NAME ${BINARY_NAME}.elf) +add_custom_command(TARGET ${BINARY_NAME}.elf POST_BUILD COMMAND ${FIXUP} -q -S ${BINARY_NAME}.elf ${BINARY_NAME}.velf MAIN_DEPENDENCY ${BINARY_NAME}.elf) diff --git a/src/platform/psp2/CMakeToolchain.txt b/src/platform/psp2/CMakeToolchain.txt new file mode 100644 index 000000000..e6490008b --- /dev/null +++ b/src/platform/psp2/CMakeToolchain.txt @@ -0,0 +1,44 @@ +if(DEFINED ENV{DEVKITPRO}) + set(DEVKITPRO $ENV{DEVKITPRO}) +else() + message(FATAL_ERROR "Could not find DEVKITPRO in environment") +endif() + +if(DEFINED ENV{DEVKITARM}) + set(DEVKITARM $ENV{DEVKITARM}) +else() + set(DEVKITARM ${DEVKITPRO}/devkitARM) +endif() + +set(toolchain_dir ${DEVKITARM}/psp2) +set(toolchain_bin_dir ${toolchain_dir}/bin) +set(cross_prefix ${DEVKITARM}/bin/arm-none-eabi-) +set(inc_flags -I${toolchain_dir}/include) +set(arch_flags "-march=armv7-a -mtune=cortex-a9") +set(link_flags "-L${toolchain_dir}/lib -specs=psp2.specs ${arch_flags}") + +set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") +set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor") +set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi") +set(CMAKE_AR ${cross_prefix}ar CACHE INTERNAL "archiver") +set(CMAKE_C_COMPILER ${cross_prefix}gcc CACHE INTERNAL "c compiler") +set(CMAKE_CXX_COMPILER ${cross_prefix}g++ CACHE INTERNAL "cxx compiler") +set(CMAKE_ASM_COMPILER ${cross_prefix}gcc CACHE INTERNAL "assembler") +set(common_flags "${arch_flags} -fno-reorder-functions -fno-optimize-sibling-calls ${inc_flags}") +set(CMAKE_C_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_ASM_FLAGS ${common_flags} CACHE INTERNAL "c compiler flags") +set(CMAKE_CXX_FLAGS ${common_flags} CACHE INTERNAL "cxx compiler flags") +set(CMAKE_LINKER ${cross_prefix}ld CACHE INTERNAL "linker") + +set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags") +set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags") +set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags") + +set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE) + +set(FIXUP ${toolchain_bin_dir}/psp2-fixup) + +set(PSP2 ON) +add_definitions(-DPSP2) + +set(CMAKE_C_COMPILER_WORKS 1) # Skip test diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c new file mode 100644 index 000000000..82a407c4f --- /dev/null +++ b/src/platform/psp2/main.c @@ -0,0 +1,135 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gba/gba.h" +#include "gba/video.h" + +#include "gba/renderers/video-software.h" +#include "util/memory.h" +#include "util/vfs.h" +#include "platform/psp2/sce-vfs.h" + +#include +#include +#include +#include +#include +#include + +#include + +PSP2_MODULE_INFO(0, 0, "mGBA"); + +#define PSP2_HORIZONTAL_PIXELS 960 +#define PSP2_VERTICAL_PIXELS 544 + +int main() { + printf("%s initializing", projectName); + bool running = true; + + struct GBAVideoSoftwareRenderer renderer; + GBAVideoSoftwareRendererCreate(&renderer); + + struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); + struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + + printf("GBA: %08X", gba); + printf("CPU: %08X", cpu); + int activeKeys = 0; + + vita2d_init(); + vita2d_texture* tex = vita2d_create_empty_texture(256, 256); + + renderer.outputBuffer = vita2d_texture_get_datap(tex); + renderer.outputBufferStride = 256; + + struct VFile* rom = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.gba", PSP2_O_RDONLY, 0666); + struct VFile* save = VFileOpenSce("cache0:/VitaDefilerClient/Documents/GBA/rom.sav", PSP2_O_RDWR | PSP2_O_CREAT, 0666); + + printf("ROM: %08X", rom); + printf("Save: %08X", save); + + GBACreate(gba); + ARMSetComponents(cpu, &gba->d, 0, 0); + ARMInit(cpu); + printf("%s initialized.", "CPU"); + + gba->keySource = &activeKeys; + gba->sync = 0; + + GBAVideoAssociateRenderer(&gba->video, &renderer.d); + + GBALoadROM(gba, rom, save, 0); + printf("%s loaded.", "ROM"); + + ARMReset(cpu); + + printf("%s all set and ready to roll.", projectName); + + int frameCounter = 0; + while (running) { + ARMRunLoop(cpu); + + if (frameCounter != gba->video.frameCounter) { + SceCtrlData pad; + sceCtrlPeekBufferPositive(0, &pad, 1); + activeKeys = 0; + if (pad.buttons & PSP2_CTRL_CROSS) { + activeKeys |= 1 << GBA_KEY_A; + } + if (pad.buttons & PSP2_CTRL_CIRCLE) { + activeKeys |= 1 << GBA_KEY_B; + } + if (pad.buttons & PSP2_CTRL_START) { + activeKeys |= 1 << GBA_KEY_START; + } + if (pad.buttons & PSP2_CTRL_SELECT) { + activeKeys |= 1 << GBA_KEY_SELECT; + } + if (pad.buttons & PSP2_CTRL_UP) { + activeKeys |= 1 << GBA_KEY_UP; + } + if (pad.buttons & PSP2_CTRL_DOWN) { + activeKeys |= 1 << GBA_KEY_DOWN; + } + if (pad.buttons & PSP2_CTRL_LEFT) { + activeKeys |= 1 << GBA_KEY_LEFT; + } + if (pad.buttons & PSP2_CTRL_RIGHT) { + activeKeys |= 1 << GBA_KEY_RIGHT; + } + if (pad.buttons & PSP2_CTRL_LTRIGGER) { + activeKeys |= 1 << GBA_KEY_L; + } + if (pad.buttons & PSP2_CTRL_RTRIGGER) { + activeKeys |= 1 << GBA_KEY_R; + } + + vita2d_start_drawing(); + vita2d_clear_screen(); + vita2d_draw_texture_scale(tex, 120, 32, 3.0f, 3.0f); + vita2d_end_drawing(); + vita2d_swap_buffers(); + + frameCounter = gba->video.frameCounter; + } + } + printf("%s shutting down...", projectName); + + ARMDeinit(cpu); + GBADestroy(gba); + + rom->close(rom); + save->close(save); + + mappedMemoryFree(gba, 0); + mappedMemoryFree(cpu, 0); + + vita2d_fini(); + vita2d_free_texture(tex); + + sceKernelExitProcess(0); + return 0; +} diff --git a/src/platform/psp2/memory.c b/src/platform/psp2/memory.c new file mode 100644 index 000000000..6f06134e1 --- /dev/null +++ b/src/platform/psp2/memory.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/memory.h" +#include "util/vector.h" + +#include +#include + +DECLARE_VECTOR(SceUIDList, SceUID); +DEFINE_VECTOR(SceUIDList, SceUID); + +static struct SceUIDList uids; +static bool listInit = false; + +void* anonymousMemoryMap(size_t size) { + if (!listInit) { + SceUIDListInit(&uids, 8); + } + if (size & 0xFFF) { + // Align to 4kB pages + size += ((~size) & 0xFFF) + 1; + } + SceUID memblock = sceKernelAllocMemBlock("anon", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, size, 0); + if (memblock < 0) { + return 0; + } + *SceUIDListAppend(&uids) = memblock; + void* ptr; + if (sceKernelGetMemBlockBase(memblock, &ptr) < 0) { + return 0; + } + return ptr; +} + +void mappedMemoryFree(void* memory, size_t size) { + UNUSED(size); + size_t i; + for (i = 0; i < SceUIDListSize(&uids); ++i) { + SceUID uid = *SceUIDListGetPointer(&uids, i); + void* ptr; + if (sceKernelGetMemBlockBase(uid, &ptr) < 0) { + continue; + } + if (ptr == memory) { + sceKernelFreeMemBlock(uid); + SceUIDListUnshift(&uids, i, 1); + return; + } + } +} diff --git a/src/platform/psp2/sce-vfs.c b/src/platform/psp2/sce-vfs.c new file mode 100644 index 000000000..1ba99640e --- /dev/null +++ b/src/platform/psp2/sce-vfs.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "sce-vfs.h" + +#include "util/vfs.h" +#include "util/memory.h" + + +struct VFileSce { + struct VFile d; + + SceUID fd; +}; + +static bool _vfsceClose(struct VFile* vf); +static off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence); +static ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size); +static ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size); +static void* _vfsceMap(struct VFile* vf, size_t size, int flags); +static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size); +static void _vfsceTruncate(struct VFile* vf, size_t size); +static ssize_t _vfsceSize(struct VFile* vf); + +struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { + struct VFileSce* vfsce = malloc(sizeof(struct VFileSce)); + if (!vfsce) { + return 0; + } + + vfsce->fd = sceIoOpen(path, flags, mode); + if (vfsce->fd < 0) { + free(vfsce); + return 0; + } + + vfsce->d.close = _vfsceClose; + vfsce->d.seek = _vfsceSeek; + vfsce->d.read = _vfsceRead; + vfsce->d.readline = 0; + vfsce->d.write = _vfsceWrite; + vfsce->d.map = _vfsceMap; + vfsce->d.unmap = _vfsceUnmap; + vfsce->d.truncate = _vfsceTruncate; + vfsce->d.size = _vfsceSize; + + return &vfsce->d; +} + +bool _vfsceClose(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + + return sceIoClose(vfsce->fd) >= 0; +} + +off_t _vfsceSeek(struct VFile* vf, off_t offset, int whence) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoLseek(vfsce->fd, offset, whence); +} + +ssize_t _vfsceRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoRead(vfsce->fd, buffer, size); +} + +ssize_t _vfsceWrite(struct VFile* vf, const void* buffer, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + return sceIoWrite(vfsce->fd, buffer, size); +} + +static void* _vfsceMap(struct VFile* vf, size_t size, int flags) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + UNUSED(flags); + void* buffer = anonymousMemoryMap(size); + if (buffer) { + sceIoRead(vfsce->fd, buffer, size); + } + return buffer; +} + +static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + mappedMemoryFree(memory, size); +} + +static void _vfsceTruncate(struct VFile* vf, size_t size) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + // TODO +} + +ssize_t _vfsceSize(struct VFile* vf) { + struct VFileSce* vfsce = (struct VFileSce*) vf; + SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR); + SceOff end = sceIoLseek(vfsce->fd, 0, SEEK_END); + sceIoLseek(vfsce->fd, cur, SEEK_SET); + return end; +} diff --git a/src/platform/psp2/sce-vfs.h b/src/platform/psp2/sce-vfs.h new file mode 100644 index 000000000..998b8d5b2 --- /dev/null +++ b/src/platform/psp2/sce-vfs.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef SCE_VFS_H +#define SCE_VFS_H + +#ifdef PSP2 +#include +#include +#else +#include +#endif + +struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode); + +#endif diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index d07c4a0a8..191ecbc27 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioDevice.h" +#include "LogController.h" + extern "C" { #include "gba/gba.h" #include "gba/audio.h" @@ -24,6 +26,7 @@ AudioDevice::AudioDevice(QObject* parent) void AudioDevice::setFormat(const QAudioFormat& format) { if (!m_context || !GBAThreadIsActive(m_context)) { + LOG(INFO) << tr("Can't set format of context-less audio device"); return; } #if RESAMPLE_LIBRARY == RESAMPLE_NN @@ -49,6 +52,7 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { } if (!m_context->gba) { + LOG(WARN) << tr("Audio device is missing its GBA"); return 0; } @@ -68,5 +72,6 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { } qint64 AudioDevice::writeData(const char*, qint64) { + LOG(WARN) << tr("Writing data to read-only audio device"); return 0; } diff --git a/src/platform/qt/AudioProcessorQt.cpp b/src/platform/qt/AudioProcessorQt.cpp index b16a50981..23a3f8e7f 100644 --- a/src/platform/qt/AudioProcessorQt.cpp +++ b/src/platform/qt/AudioProcessorQt.cpp @@ -6,6 +6,7 @@ #include "AudioProcessorQt.h" #include "AudioDevice.h" +#include "LogController.h" #include @@ -34,6 +35,7 @@ void AudioProcessorQt::setInput(GBAThread* input) { void AudioProcessorQt::start() { if (!input()) { + LOG(WARN) << tr("Can't start an audio processor without input"); return; } diff --git a/src/platform/qt/AudioProcessorSDL.cpp b/src/platform/qt/AudioProcessorSDL.cpp index ff88cb376..ce6e66bd4 100644 --- a/src/platform/qt/AudioProcessorSDL.cpp +++ b/src/platform/qt/AudioProcessorSDL.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioProcessorSDL.h" +#include "LogController.h" + extern "C" { #include "gba/supervisor/thread.h" } @@ -23,6 +25,7 @@ AudioProcessorSDL::~AudioProcessorSDL() { void AudioProcessorSDL::start() { if (!input()) { + LOG(WARN) << tr("Can't start an audio processor without input"); return; } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 33ff03ec8..a6ff2aea2 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -63,6 +63,7 @@ set(SOURCE_FILES InputController.cpp KeyEditor.cpp LoadSaveState.cpp + LogController.cpp LogView.cpp MemoryModel.cpp MemoryView.cpp diff --git a/src/platform/qt/CheatsModel.cpp b/src/platform/qt/CheatsModel.cpp index 38538e296..bd3fd9cbd 100644 --- a/src/platform/qt/CheatsModel.cpp +++ b/src/platform/qt/CheatsModel.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CheatsModel.h" +#include "LogController.h" #include "VFileDevice.h" #include @@ -68,7 +69,7 @@ bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int r free(cheats->name); cheats->name = nullptr; } - cheats->name = strdup(value.toString().toLocal8Bit().constData()); + cheats->name = strdup(value.toString().toUtf8().constData()); emit dataChanged(index, index); return true; case Qt::CheckStateRole: @@ -104,7 +105,7 @@ QModelIndex CheatsModel::parent(const QModelIndex& index) const { return QModelIndex(); } -Qt::ItemFlags CheatsModel::flags(const QModelIndex &index) const { +Qt::ItemFlags CheatsModel::flags(const QModelIndex& index) const { if (!index.isValid()) { return 0; } @@ -152,7 +153,6 @@ void CheatsModel::removeAt(const QModelIndex& index) { GBACheatSetDeinit(set); delete set; endInsertRows(); - } QString CheatsModel::toString(const QModelIndexList& indices) const { @@ -204,6 +204,7 @@ void CheatsModel::endAppendRow() { void CheatsModel::loadFile(const QString& path) { VFile* vf = VFileDevice::open(path, O_RDONLY); if (!vf) { + LOG(WARN) << tr("Failed to open cheats file: %1").arg(path); return; } beginResetModel(); diff --git a/src/platform/qt/CheatsModel.h b/src/platform/qt/CheatsModel.h index 62337c794..78c906448 100644 --- a/src/platform/qt/CheatsModel.h +++ b/src/platform/qt/CheatsModel.h @@ -27,7 +27,7 @@ public: virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; GBACheatSet* itemAt(const QModelIndex& index); void removeAt(const QModelIndex& index); diff --git a/src/platform/qt/CheatsView.cpp b/src/platform/qt/CheatsView.cpp index 12981efa8..c39ad398b 100644 --- a/src/platform/qt/CheatsView.cpp +++ b/src/platform/qt/CheatsView.cpp @@ -112,7 +112,7 @@ void CheatsView::enterCheat(std::function callb QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts); for (const QString& string : cheats) { m_model.beginAppendRow(selection[0]); - callback(set, string.toLocal8Bit().constData()); + callback(set, string.toUtf8().constData()); m_model.endAppendRow(); } m_controller->threadContinue(); diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index ba4978bac..1996160a9 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -80,7 +80,7 @@ void ConfigOption::setValue(const char* value) { void ConfigOption::setValue(const QVariant& value) { QPair action; - foreach(action, m_actions) { + foreach (action, m_actions) { bool signalsEnabled = action.first->blockSignals(true); action.first->setChecked(value == action.second); action.first->blockSignals(signalsEnabled); @@ -214,7 +214,7 @@ void ConfigController::setOption(const char* key, const QVariant& value) { return; } QString stringValue(value.toString()); - setOption(key, stringValue.toLocal8Bit().constData()); + setOption(key, stringValue.toUtf8().constData()); } void ConfigController::setQtOption(const QString& key, const QVariant& value, const QString& group) { diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 59d218d46..7f0a37f50 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -46,7 +46,26 @@ Display* Display::create(QWidget* parent) { Display::Display(QWidget* parent) : QWidget(parent) + , m_lockAspectRatio(false) + , m_filter(false) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); } + +void Display::resizeEvent(QResizeEvent*) { + m_messagePainter.resize(size(), m_lockAspectRatio, devicePixelRatio()); +} + +void Display::lockAspectRatio(bool lock) { + m_lockAspectRatio = lock; + m_messagePainter.resize(size(), m_lockAspectRatio, devicePixelRatio()); +} + +void Display::filter(bool filter) { + m_filter = filter; +} + +void Display::showMessage(const QString& message) { + m_messagePainter.showMessage(message); +} diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index c69461f39..6ae6a35c2 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -8,6 +8,8 @@ #include +#include "MessagePainter.h" + struct GBAThread; namespace QGBA { @@ -28,20 +30,32 @@ public: static Display* create(QWidget* parent = nullptr); static void setDriver(Driver driver) { s_driver = driver; } + bool isAspectRatioLocked() const { return m_lockAspectRatio; } + bool isFiltered() const { return m_filter; } + public slots: virtual void startDrawing(GBAThread* context) = 0; virtual void stopDrawing() = 0; virtual void pauseDrawing() = 0; virtual void unpauseDrawing() = 0; virtual void forceDraw() = 0; - virtual void lockAspectRatio(bool lock) = 0; - virtual void filter(bool filter) = 0; + virtual void lockAspectRatio(bool lock); + virtual void filter(bool filter); virtual void framePosted(const uint32_t*) = 0; - virtual void showMessage(const QString& message) = 0; + void showMessage(const QString& message); + +protected: + void resizeEvent(QResizeEvent*); + + MessagePainter* messagePainter() { return &m_messagePainter; } private: static Driver s_driver; + + MessagePainter m_messagePainter; + bool m_lockAspectRatio; + bool m_filter; }; } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 532c4c05a..456840751 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -19,8 +19,6 @@ DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent) , m_gl(new EmptyGLWidget(format, this)) , m_painter(new PainterGL(m_gl)) , m_drawThread(nullptr) - , m_lockAspectRatio(false) - , m_filter(false) , m_context(nullptr) { } @@ -34,6 +32,7 @@ void DisplayGL::startDrawing(GBAThread* thread) { return; } m_painter->setContext(thread); + m_painter->setMessagePainter(messagePainter()); m_context = thread; m_painter->resize(size()); m_gl->move(0, 0); @@ -46,8 +45,9 @@ void DisplayGL::startDrawing(GBAThread* thread) { m_drawThread->start(); GBASyncSetVideoSync(&m_context->sync, false); - lockAspectRatio(m_lockAspectRatio); - filter(m_filter); + lockAspectRatio(isAspectRatioLocked()); + filter(isFiltered()); + messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio()); resizePainter(); } @@ -96,14 +96,14 @@ void DisplayGL::forceDraw() { } void DisplayGL::lockAspectRatio(bool lock) { - m_lockAspectRatio = lock; + Display::lockAspectRatio(lock); if (m_drawThread) { QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Q_ARG(bool, lock)); } } void DisplayGL::filter(bool filter) { - m_filter = filter; + Display::filter(filter); if (m_drawThread) { QMetaObject::invokeMethod(m_painter, "filter", Q_ARG(bool, filter)); } @@ -115,13 +115,8 @@ void DisplayGL::framePosted(const uint32_t* buffer) { } } -void DisplayGL::showMessage(const QString& message) { - if (m_drawThread) { - QMetaObject::invokeMethod(m_painter, "showMessage", Q_ARG(const QString&, message)); - } -} - -void DisplayGL::resizeEvent(QResizeEvent*) { +void DisplayGL::resizeEvent(QResizeEvent* event) { + Display::resizeEvent(event); resizePainter(); } @@ -152,6 +147,10 @@ void PainterGL::setContext(GBAThread* context) { m_context = context; } +void PainterGL::setMessagePainter(MessagePainter* messagePainter) { + m_messagePainter = messagePainter; +} + void PainterGL::setBacking(const uint32_t* backing) { m_gl->makeCurrent(); m_backend.d.postFrame(&m_backend.d, backing); @@ -164,7 +163,6 @@ void PainterGL::setBacking(const uint32_t* backing) { void PainterGL::resize(const QSize& size) { m_size = size; if (m_active) { - m_messagePainter->resize(size, m_backend.d.lockAspectRatio); forceDraw(); } } @@ -172,7 +170,6 @@ void PainterGL::resize(const QSize& size) { void PainterGL::lockAspectRatio(bool lock) { m_backend.d.lockAspectRatio = lock; if (m_active) { - m_messagePainter->resize(m_size, m_backend.d.lockAspectRatio); forceDraw(); } } @@ -185,8 +182,6 @@ void PainterGL::filter(bool filter) { } void PainterGL::start() { - m_messagePainter = new MessagePainter(this); - m_messagePainter->resize(m_size, m_backend.d.lockAspectRatio); m_gl->makeCurrent(); m_backend.d.init(&m_backend.d, reinterpret_cast(m_gl->winId())); m_gl->doneCurrent(); @@ -220,9 +215,6 @@ void PainterGL::stop() { m_backend.d.deinit(&m_backend.d); m_gl->doneCurrent(); m_gl->context()->moveToThread(m_gl->thread()); - m_messagePainter->clearMessage(); - delete m_messagePainter; - m_messagePainter = nullptr; moveToThread(m_gl->thread()); } @@ -243,9 +235,7 @@ void PainterGL::performDraw() { m_backend.d.resized(&m_backend.d, m_size.width() * r, m_size.height() * r); m_backend.d.drawFrame(&m_backend.d); m_painter.endNativePainting(); - m_messagePainter->paint(&m_painter); -} - -void PainterGL::showMessage(const QString& message) { - m_messagePainter->showMessage(message); + if (m_messagePainter) { + m_messagePainter->paint(&m_painter); + } } diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 16c7a1d0c..c49bab16c 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -8,8 +8,6 @@ #include "Display.h" -#include "MessagePainter.h" - #include #include #include @@ -49,8 +47,6 @@ public slots: void filter(bool filter) override; void framePosted(const uint32_t*) override; - void showMessage(const QString& message) override; - protected: virtual void paintEvent(QPaintEvent*) override {} virtual void resizeEvent(QResizeEvent*) override; @@ -62,8 +58,6 @@ private: PainterGL* m_painter; QThread* m_drawThread; GBAThread* m_context; - bool m_lockAspectRatio; - bool m_filter; }; class PainterGL : public QObject { @@ -73,6 +67,7 @@ public: PainterGL(QGLWidget* parent); void setContext(GBAThread*); + void setMessagePainter(MessagePainter*); public slots: void setBacking(const uint32_t*); @@ -86,8 +81,6 @@ public slots: void lockAspectRatio(bool lock); void filter(bool filter); - void showMessage(const QString& message); - private: void performDraw(); diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index 438c44e47..182ecd2bc 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -7,13 +7,15 @@ #include +extern "C" { +#include "gba/video.h" +} + using namespace QGBA; DisplayQt::DisplayQt(QWidget* parent) : Display(parent) , m_backing(nullptr) - , m_lockAspectRatio(false) - , m_filter(false) { } @@ -21,12 +23,12 @@ void DisplayQt::startDrawing(GBAThread*) { } void DisplayQt::lockAspectRatio(bool lock) { - m_lockAspectRatio = lock; + Display::lockAspectRatio(lock); update(); } void DisplayQt::filter(bool filter) { - m_filter = filter; + Display::filter(filter); update(); } @@ -46,19 +48,15 @@ void DisplayQt::framePosted(const uint32_t* buffer) { #endif } -void DisplayQt::showMessage(const QString& message) { - m_messagePainter.showMessage(message); -} - void DisplayQt::paintEvent(QPaintEvent*) { QPainter painter(this); painter.fillRect(QRect(QPoint(), size()), Qt::black); - if (m_filter) { + if (isFiltered()) { painter.setRenderHint(QPainter::SmoothPixmapTransform); } QSize s = size(); QSize ds = s; - if (m_lockAspectRatio) { + if (isAspectRatioLocked()) { if (s.width() * 2 > s.height() * 3) { ds.setWidth(s.height() * 3 / 2); } else if (s.width() * 2 < s.height() * 3) { @@ -69,13 +67,9 @@ void DisplayQt::paintEvent(QPaintEvent*) { QRect full(origin, ds); #ifdef COLOR_5_6_5 - painter.drawImage(full, m_backing, QRect(0, 0, 240, 160)); + painter.drawImage(full, m_backing, QRect(0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS)); #else - painter.drawImage(full, m_backing.rgbSwapped(), QRect(0, 0, 240, 160)); + painter.drawImage(full, m_backing.rgbSwapped(), QRect(0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS)); #endif - m_messagePainter.paint(&painter); -} - -void DisplayQt::resizeEvent(QResizeEvent*) { - m_messagePainter.resize(size(), m_lockAspectRatio); + messagePainter()->paint(&painter); } diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index e10b13b08..f7fefb0ee 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -7,7 +7,6 @@ #define QGBA_DISPLAY_QT #include "Display.h" -#include "MessagePainter.h" #include #include @@ -32,17 +31,11 @@ public slots: void filter(bool filter) override; void framePosted(const uint32_t*) override; - void showMessage(const QString& message) override; - protected: virtual void paintEvent(QPaintEvent*) override; - virtual void resizeEvent(QResizeEvent*) override;; private: QImage m_backing; - bool m_lockAspectRatio; - bool m_filter; - MessagePainter m_messagePainter; }; } diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 326bfbbcb..c40eb42ba 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -41,6 +41,10 @@ GBAApp::GBAApp(int& argc, char* argv[]) QApplication::setApplicationName(projectName); QApplication::setApplicationVersion(projectVersion); + if (!m_configController.getQtOption("displayDriver").isNull()) { + Display::setDriver(static_cast(m_configController.getQtOption("displayDriver").toInt())); + } + Window* w = new Window(&m_configController); connect(w, &Window::destroyed, [this]() { m_windows[0] = nullptr; @@ -59,7 +63,6 @@ GBAApp::GBAApp(int& argc, char* argv[]) } freeArguments(&args); - Display::setDriver(static_cast(m_configController.getQtOption("videoDriver").toInt())); AudioProcessor::setDriver(static_cast(m_configController.getQtOption("audioDriver").toInt())); w->controller()->reloadAudioDriver(); diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 1cf0d8d30..129e94a6c 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -46,7 +46,8 @@ protected: private: class FileDialog : public QFileDialog { public: - FileDialog(GBAApp* app, QWidget* parent = nullptr, const QString& caption = QString(), const QString& filter = QString()); + FileDialog(GBAApp* app, QWidget* parent = nullptr, const QString& caption = QString(), + const QString& filter = QString()); virtual int exec() override; private: diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 47037991e..4a8577979 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -205,7 +205,7 @@ void GBAKeyEditor::refresh() { } void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, GBAKey key) { - #ifdef BUILD_SDL +#ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON) { int value = GBAInputQueryBinding(map, m_type, key); if (value != GBA_NO_MAPPING) { @@ -213,7 +213,7 @@ void GBAKeyEditor::lookupBinding(const GBAInputMap* map, KeyEditor* keyEditor, G } return; } - #endif +#endif keyEditor->setValueKey(GBAInputQueryBinding(map, m_type, key)); } @@ -304,5 +304,6 @@ KeyEditor* GBAKeyEditor::keyById(GBAKey key) { void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { QSize s = size(); QSize hint = widget->sizeHint(); - widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(), hint.height()); + widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(), + hint.height()); } diff --git a/src/platform/qt/GDBController.cpp b/src/platform/qt/GDBController.cpp index 2364393cc..197b7928d 100644 --- a/src/platform/qt/GDBController.cpp +++ b/src/platform/qt/GDBController.cpp @@ -44,7 +44,7 @@ void GDBController::attach() { ARMDebuggerEnter(&m_gdbStub.d, DEBUGGER_ENTER_ATTACHED, 0); } else { QObject::disconnect(m_autoattach); - m_autoattach = connect(m_gameController, &GameController::gameStarted, [this] () { + m_autoattach = connect(m_gameController, &GameController::gameStarted, [this]() { QObject::disconnect(m_autoattach); ARMDebuggerEnter(&m_gdbStub.d, DEBUGGER_ENTER_ATTACHED, 0); }); diff --git a/src/platform/qt/GDBWindow.cpp b/src/platform/qt/GDBWindow.cpp index de467bb12..26191b6e0 100644 --- a/src/platform/qt/GDBWindow.cpp +++ b/src/platform/qt/GDBWindow.cpp @@ -106,9 +106,8 @@ void GDBWindow::stopped() { } void GDBWindow::failed() { - QMessageBox* failure = new QMessageBox(QMessageBox::Warning, tr("Crash"), - tr("Could not start GDB server"), - QMessageBox::Ok, this, Qt::Sheet); + QMessageBox* failure = new QMessageBox(QMessageBox::Warning, tr("Crash"), tr("Could not start GDB server"), + QMessageBox::Ok, this, Qt::Sheet); failure->setAttribute(Qt::WA_DeleteOnClose); failure->show(); } diff --git a/src/platform/qt/GIFView.cpp b/src/platform/qt/GIFView.cpp index 8c08d3947..8bec4a484 100644 --- a/src/platform/qt/GIFView.cpp +++ b/src/platform/qt/GIFView.cpp @@ -8,6 +8,7 @@ #ifdef USE_MAGICK #include "GBAApp.h" +#include "LogController.h" #include @@ -33,7 +34,8 @@ GIFView::~GIFView() { } void GIFView::startRecording() { - if (!ImageMagickGIFEncoderOpen(&m_encoder, m_filename.toLocal8Bit().constData())) { + if (!ImageMagickGIFEncoderOpen(&m_encoder, m_filename.toUtf8().constData())) { + LOG(ERROR) << tr("Failed to open output GIF file: %1").arg(m_filename); return; } m_ui.start->setEnabled(false); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 860a08d66..8440cd284 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -70,50 +70,35 @@ GameController::GameController(QObject* parent) m_threadContext.logLevel = GBA_LOG_ALL; m_lux.p = this; - m_lux.sample = [] (GBALuminanceSource* context) { + m_lux.sample = [](GBALuminanceSource* context) { GameControllerLux* lux = static_cast(context); lux->value = 0xFF - lux->p->m_luxValue; }; - m_lux.readLuminance = [] (GBALuminanceSource* context) { + m_lux.readLuminance = [](GBALuminanceSource* context) { GameControllerLux* lux = static_cast(context); return lux->value; }; setLuminanceLevel(0); - m_rtc.p = this; - m_rtc.override = GameControllerRTC::NO_OVERRIDE; - m_rtc.sample = [] (GBARTCSource* context) { }; - m_rtc.unixTime = [] (GBARTCSource* context) -> time_t { - GameControllerRTC* rtc = static_cast(context); - switch (rtc->override) { - case GameControllerRTC::NO_OVERRIDE: - default: - return time(nullptr); - case GameControllerRTC::FIXED: - return rtc->value; - case GameControllerRTC::FAKE_EPOCH: - return rtc->value + rtc->p->m_threadContext.gba->video.frameCounter * (int64_t) VIDEO_TOTAL_LENGTH / GBA_ARM7TDMI_FREQUENCY; - } - }; - - m_threadContext.startCallback = [] (GBAThread* context) { + m_threadContext.startCallback = [](GBAThread* context) { GameController* controller = static_cast(context->userData); controller->m_audioProcessor->setInput(context); context->gba->luminanceSource = &controller->m_lux; - context->gba->rtcSource = &controller->m_rtc; + GBARTCGenericSourceInit(&controller->m_rtc, context->gba); + context->gba->rtcSource = &controller->m_rtc.d; context->gba->rumble = controller->m_inputController->rumble(); context->gba->rotationSource = controller->m_inputController->rotationSource(); controller->m_fpsTarget = context->fpsTarget; controller->gameStarted(context); }; - m_threadContext.cleanCallback = [] (GBAThread* context) { + m_threadContext.cleanCallback = [](GBAThread* context) { GameController* controller = static_cast(context->userData); controller->gameStopped(context); }; - m_threadContext.frameCallback = [] (GBAThread* context) { + m_threadContext.frameCallback = [](GBAThread* context) { GameController* controller = static_cast(context->userData); if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) { GBAThreadPauseFromThread(context); @@ -126,7 +111,7 @@ GameController::GameController(QObject* parent) } }; - m_threadContext.logHandler = [] (GBAThread* context, enum GBALogLevel level, const char* format, va_list args) { + m_threadContext.logHandler = [](GBAThread* context, enum GBALogLevel level, const char* format, va_list args) { static const char* stubMessage = "Stub software interrupt"; if (!context) { return; @@ -136,6 +121,7 @@ GameController::GameController(QObject* parent) va_list argc; va_copy(argc, args); int immediate = va_arg(argc, int); + va_end(argc); controller->unimplementedBiosCall(immediate); } if (level == GBA_LOG_FATAL) { @@ -152,8 +138,8 @@ GameController::GameController(QObject* parent) connect(&m_rewindTimer, &QTimer::timeout, [this]() { GBARewind(&m_threadContext, 1); - emit rewound(&m_threadContext); emit frameAvailable(m_drawContext); + emit rewound(&m_threadContext); }); m_rewindTimer.setInterval(100); @@ -238,6 +224,7 @@ void GameController::loadGame(const QString& path, bool dirmode) { if (!dirmode) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { + postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path)); return; } file.close(); @@ -277,7 +264,7 @@ void GameController::openGame(bool biosOnly) { if (biosOnly) { m_threadContext.fname = nullptr; } else { - m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData()); + m_threadContext.fname = strdup(m_fname.toUtf8().constData()); if (m_dirmode) { m_threadContext.gameDir = VDirOpen(m_threadContext.fname); m_threadContext.stateDir = m_threadContext.gameDir; @@ -324,7 +311,6 @@ void GameController::yankPak() { threadContinue(); } - void GameController::replaceGame(const QString& path) { if (!m_gameOpen) { return; @@ -353,6 +339,7 @@ void GameController::importSharkport(const QString& path) { } VFile* vf = VFileDevice::open(path, O_RDONLY); if (!vf) { + postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for reading: %1").arg(path)); return; } threadInterrupt(); @@ -367,6 +354,7 @@ void GameController::exportSharkport(const QString& path) { } VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { + postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for writing: %1").arg(path)); return; } threadInterrupt(); @@ -478,8 +466,8 @@ void GameController::rewind(int states) { GBARewind(&m_threadContext, states); } threadContinue(); - emit rewound(&m_threadContext); emit frameAvailable(m_drawContext); + emit rewound(&m_threadContext); } void GameController::startRewinding() { @@ -574,8 +562,8 @@ void GameController::loadState(int slot) { GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast(context->userData); if (GBALoadState(context, context->stateDir, controller->m_stateSlot)) { - controller->stateLoaded(context); controller->frameAvailable(controller->m_drawContext); + controller->stateLoaded(context); } }); } @@ -731,16 +719,16 @@ void GameController::setLuminanceLevel(int level) { } void GameController::setRealTime() { - m_rtc.override = GameControllerRTC::NO_OVERRIDE; + m_rtc.override = GBARTCGenericSource::RTC_NO_OVERRIDE; } void GameController::setFixedTime(const QDateTime& time) { - m_rtc.override = GameControllerRTC::FIXED; + m_rtc.override = GBARTCGenericSource::RTC_FIXED; m_rtc.value = time.toMSecsSinceEpoch() / 1000; } void GameController::setFakeEpoch(const QDateTime& time) { - m_rtc.override = GameControllerRTC::FAKE_EPOCH; + m_rtc.override = GBARTCGenericSource::RTC_FAKE_EPOCH; m_rtc.value = time.toMSecsSinceEpoch() / 1000; } diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index e80769dca..349a39ce6 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -205,15 +205,7 @@ private: static const int LUX_LEVELS[10]; - struct GameControllerRTC : GBARTCSource { - GameController* p; - enum { - NO_OVERRIDE, - FIXED, - FAKE_EPOCH - } override; - int64_t value; - } m_rtc; + GBARTCGenericSource m_rtc; }; } diff --git a/src/platform/qt/GamepadAxisEvent.h b/src/platform/qt/GamepadAxisEvent.h index 15729d257..09a1b015d 100644 --- a/src/platform/qt/GamepadAxisEvent.h +++ b/src/platform/qt/GamepadAxisEvent.h @@ -16,7 +16,7 @@ namespace QGBA { class InputController; -class GamepadAxisEvent : public QEvent { +class GamepadAxisEvent : public QEvent { public: enum Direction { NEUTRAL = 0, diff --git a/src/platform/qt/GamepadButtonEvent.h b/src/platform/qt/GamepadButtonEvent.h index ec3ce2f2e..375a0e05f 100644 --- a/src/platform/qt/GamepadButtonEvent.h +++ b/src/platform/qt/GamepadButtonEvent.h @@ -16,7 +16,7 @@ namespace QGBA { class InputController; -class GamepadButtonEvent : public QEvent { +class GamepadButtonEvent : public QEvent { public: GamepadButtonEvent(Type pressType, int button, int type, InputController* controller = nullptr); diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 9cb20f999..ec5ef6b37 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -106,7 +106,7 @@ void InputController::loadConfiguration(uint32_t type) { } void InputController::loadProfile(uint32_t type, const QString& profile) { - GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData()); + GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); recalibrateAxes(); } @@ -128,7 +128,7 @@ void InputController::saveConfiguration(uint32_t type) { } void InputController::saveProfile(uint32_t type, const QString& profile) { - GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData()); + GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); m_config->write(); } @@ -193,7 +193,7 @@ void InputController::setPreferredGamepad(uint32_t type, const QString& device) if (!m_config) { return; } - GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toLocal8Bit().constData()); + GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toUtf8().constData()); } GBARumble* InputController::rumble() { diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp new file mode 100644 index 000000000..2c3219fae --- /dev/null +++ b/src/platform/qt/LogController.cpp @@ -0,0 +1,93 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "LogController.h" + +using namespace QGBA; + +LogController LogController::s_global(GBA_LOG_ALL); + +LogController::LogController(int levels, QObject* parent) + : QObject(parent) + , m_logLevel(levels) +{ + if (this != &s_global) { + connect(&s_global, SIGNAL(logPosted(int, const QString&)), this, SLOT(postLog(int, const QString&))); + connect(this, SIGNAL(levelsSet(int)), &s_global, SLOT(setLevels(int))); + connect(this, SIGNAL(levelsEnabled(int)), &s_global, SLOT(enableLevels(int))); + connect(this, SIGNAL(levelsDisabled(int)), &s_global, SLOT(disableLevels(int))); + } +} + +LogController::Stream LogController::operator()(int level) { + return Stream(this, level); +} + +void LogController::postLog(int level, const QString& string) { + if (!(m_logLevel & level)) { + return; + } + emit logPosted(level, string); +} + +void LogController::setLevels(int levels) { + m_logLevel = levels; + emit levelsSet(levels); +} + +void LogController::enableLevels(int levels) { + m_logLevel |= levels; + emit levelsEnabled(levels); +} + +void LogController::disableLevels(int levels) { + m_logLevel &= ~levels; + emit levelsDisabled(levels); +} + +LogController* LogController::global() { + return &s_global; +} + +QString LogController::toString(int level) { + switch (level) { + case GBA_LOG_DEBUG: + return tr("DEBUG"); + case GBA_LOG_STUB: + return tr("STUB"); + case GBA_LOG_INFO: + return tr("INFO"); + case GBA_LOG_WARN: + return tr("WARN"); + case GBA_LOG_ERROR: + return tr("ERROR"); + case GBA_LOG_FATAL: + return tr("FATAL"); + case GBA_LOG_GAME_ERROR: + return tr("GAME ERROR"); + case GBA_LOG_SWI: + return tr("SWI"); + case GBA_LOG_STATUS: + return tr("STATUS"); + case GBA_LOG_SIO: + return tr("SIO"); + } + return QString(); +} + +LogController::Stream::Stream(LogController* controller, int level) + : m_log(controller) + , m_level(level) +{ +} + +LogController::Stream::~Stream() { + m_log->postLog(m_level, m_queue.join(" ")); +} + +LogController::Stream& LogController::Stream::operator<<(const QString& string) { + m_queue.append(string); + return *this; +} diff --git a/src/platform/qt/LogController.h b/src/platform/qt/LogController.h new file mode 100644 index 000000000..63dbd5a04 --- /dev/null +++ b/src/platform/qt/LogController.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_LOG_CONTROLLER +#define QGBA_LOG_CONTROLLER + +#include +#include + +extern "C" { +#include "gba/gba.h" +} + +namespace QGBA { + +class LogController : public QObject { +Q_OBJECT + +private: + class Stream { + public: + Stream(LogController* controller, int level); + ~Stream(); + + Stream& operator<<(const QString&); + + private: + int m_level; + LogController* m_log; + + QStringList m_queue; + }; + +public: + LogController(int levels, QObject* parent = nullptr); + + int levels() const { return m_logLevel; } + + Stream operator()(int level); + + static LogController* global(); + static QString toString(int level); + +signals: + void logPosted(int level, const QString& log); + void levelsSet(int levels); + void levelsEnabled(int levels); + void levelsDisabled(int levels); + +public slots: + void postLog(int level, const QString& string); + void setLevels(int levels); + void enableLevels(int levels); + void disableLevels(int levels); + +private: + int m_logLevel; + + static LogController s_global; +}; + +#define LOG(L) (*LogController::global())(GBA_LOG_ ## L) + +} + +#endif diff --git a/src/platform/qt/LogView.cpp b/src/platform/qt/LogView.cpp index bd9832def..c2f3e05e9 100644 --- a/src/platform/qt/LogView.cpp +++ b/src/platform/qt/LogView.cpp @@ -5,38 +5,71 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "LogView.h" +#include "LogController.h" + #include #include using namespace QGBA; -LogView::LogView(QWidget* parent) +LogView::LogView(LogController* log, QWidget* parent) : QWidget(parent) - , m_logLevel(0) , m_lines(0) , m_lineLimit(DEFAULT_LINE_LIMIT) { m_ui.setupUi(this); - connect(m_ui.levelDebug, SIGNAL(toggled(bool)), this, SLOT(setLevelDebug(bool))); - connect(m_ui.levelStub, SIGNAL(toggled(bool)), this, SLOT(setLevelStub(bool))); - connect(m_ui.levelInfo, SIGNAL(toggled(bool)), this, SLOT(setLevelInfo(bool))); - connect(m_ui.levelWarn, SIGNAL(toggled(bool)), this, SLOT(setLevelWarn(bool))); - connect(m_ui.levelError, SIGNAL(toggled(bool)), this, SLOT(setLevelError(bool))); - connect(m_ui.levelFatal, SIGNAL(toggled(bool)), this, SLOT(setLevelFatal(bool))); - connect(m_ui.levelGameError, SIGNAL(toggled(bool)), this, SLOT(setLevelGameError(bool))); - connect(m_ui.levelSWI, SIGNAL(toggled(bool)), this, SLOT(setLevelSWI(bool))); - connect(m_ui.levelStatus, SIGNAL(toggled(bool)), this, SLOT(setLevelStatus(bool))); - connect(m_ui.levelSIO, SIGNAL(toggled(bool)), this, SLOT(setLevelSIO(bool))); + connect(m_ui.levelDebug, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_DEBUG, set); + }); + connect(m_ui.levelStub, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_STUB, set); + }); + connect(m_ui.levelInfo, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_INFO, set); + }); + connect(m_ui.levelWarn, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_WARN, set); + }); + connect(m_ui.levelError, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_ERROR, set); + }); + connect(m_ui.levelFatal, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_FATAL, set); + }); + connect(m_ui.levelGameError, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_GAME_ERROR, set); + }); + connect(m_ui.levelSWI, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_SWI, set); + }); + connect(m_ui.levelStatus, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_STATUS, set); + }); + connect(m_ui.levelSIO, &QAbstractButton::toggled, [this](bool set) { + setLevel(GBA_LOG_SIO, set); + }); connect(m_ui.clear, SIGNAL(clicked()), this, SLOT(clear())); connect(m_ui.maxLines, SIGNAL(valueChanged(int)), this, SLOT(setMaxLines(int))); m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT); + + connect(log, SIGNAL(logPosted(int, const QString&)), this, SLOT(postLog(int, const QString&))); + connect(log, SIGNAL(levelsSet(int)), this, SLOT(setLevels(int))); + connect(log, &LogController::levelsEnabled, [this](int level) { + bool s = blockSignals(true); + setLevel(level, true); + blockSignals(s); + }); + connect(log, &LogController::levelsDisabled, [this](int level) { + bool s = blockSignals(true); + setLevel(level, false); + blockSignals(s); + }); + connect(this, SIGNAL(levelsEnabled(int)), log, SLOT(enableLevels(int))); + connect(this, SIGNAL(levelsDisabled(int)), log, SLOT(disableLevels(int))); } void LogView::postLog(int level, const QString& log) { - if (!(level & m_logLevel)) { - return; - } - m_ui.view->appendPlainText(QString("%1:\t%2").arg(toString(level)).arg(log)); + m_ui.view->appendPlainText(QString("%1:\t%2").arg(LogController::toString(level)).arg(log)); ++m_lines; if (m_lines > m_lineLimit) { clearLine(); @@ -49,8 +82,6 @@ void LogView::clear() { } void LogView::setLevels(int levels) { - m_logLevel = levels; - m_ui.levelDebug->setCheckState(levels & GBA_LOG_DEBUG ? Qt::Checked : Qt::Unchecked); m_ui.levelStub->setCheckState(levels & GBA_LOG_STUB ? Qt::Checked : Qt::Unchecked); m_ui.levelInfo->setCheckState(levels & GBA_LOG_INFO ? Qt::Checked : Qt::Unchecked); @@ -61,87 +92,44 @@ void LogView::setLevels(int levels) { m_ui.levelSWI->setCheckState(levels & GBA_LOG_SWI ? Qt::Checked : Qt::Unchecked); m_ui.levelStatus->setCheckState(levels & GBA_LOG_STATUS ? Qt::Checked : Qt::Unchecked); m_ui.levelSIO->setCheckState(levels & GBA_LOG_SIO ? Qt::Checked : Qt::Unchecked); - - emit levelsSet(levels); } -void LogView::setLevelDebug(bool set) { - if (set) { - setLevel(GBA_LOG_DEBUG); - } else { - clearLevel(GBA_LOG_DEBUG); +void LogView::setLevel(int level, bool set) { + if (level & GBA_LOG_DEBUG) { + m_ui.levelDebug->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelStub(bool set) { - if (set) { - setLevel(GBA_LOG_STUB); - } else { - clearLevel(GBA_LOG_STUB); + if (level & GBA_LOG_STUB) { + m_ui.levelStub->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelInfo(bool set) { - if (set) { - setLevel(GBA_LOG_INFO); - } else { - clearLevel(GBA_LOG_INFO); + if (level & GBA_LOG_INFO) { + m_ui.levelInfo->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelWarn(bool set) { - if (set) { - setLevel(GBA_LOG_WARN); - } else { - clearLevel(GBA_LOG_WARN); + if (level & GBA_LOG_WARN) { + m_ui.levelWarn->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelError(bool set) { - if (set) { - setLevel(GBA_LOG_ERROR); - } else { - clearLevel(GBA_LOG_ERROR); + if (level & GBA_LOG_ERROR) { + m_ui.levelError->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelFatal(bool set) { - if (set) { - setLevel(GBA_LOG_FATAL); - } else { - clearLevel(GBA_LOG_FATAL); + if (level & GBA_LOG_FATAL) { + m_ui.levelFatal->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelGameError(bool set) { - if (set) { - setLevel(GBA_LOG_GAME_ERROR); - } else { - clearLevel(GBA_LOG_GAME_ERROR); + if (level & GBA_LOG_GAME_ERROR) { + m_ui.levelGameError->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelSWI(bool set) { - if (set) { - setLevel(GBA_LOG_SWI); - } else { - clearLevel(GBA_LOG_SWI); + if (level & GBA_LOG_SWI) { + m_ui.levelSWI->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} - -void LogView::setLevelStatus(bool set) { - if (set) { - setLevel(GBA_LOG_STATUS); - } else { - clearLevel(GBA_LOG_STATUS); + if (level & GBA_LOG_STATUS) { + m_ui.levelStatus->setCheckState(set ? Qt::Checked : Qt::Unchecked); + } + if (level & GBA_LOG_SIO) { + m_ui.levelSIO->setCheckState(set ? Qt::Checked : Qt::Unchecked); } -} -void LogView::setLevelSIO(bool set) { if (set) { - setLevel(GBA_LOG_SIO); + emit levelsEnabled(level); } else { - clearLevel(GBA_LOG_SIO); + emit levelsDisabled(level); } } @@ -152,42 +140,6 @@ void LogView::setMaxLines(int limit) { } } -QString LogView::toString(int level) { - switch (level) { - case GBA_LOG_DEBUG: - return tr("DEBUG"); - case GBA_LOG_STUB: - return tr("STUB"); - case GBA_LOG_INFO: - return tr("INFO"); - case GBA_LOG_WARN: - return tr("WARN"); - case GBA_LOG_ERROR: - return tr("ERROR"); - case GBA_LOG_FATAL: - return tr("FATAL"); - case GBA_LOG_GAME_ERROR: - return tr("GAME ERROR"); - case GBA_LOG_SWI: - return tr("SWI"); - case GBA_LOG_STATUS: - return tr("STATUS"); - case GBA_LOG_SIO: - return tr("SIO"); - } - return QString(); -} - -void LogView::setLevel(int level) { - m_logLevel |= level; - emit levelsEnabled(level); -} - -void LogView::clearLevel(int level) { - m_logLevel &= ~level; - emit levelsDisabled(level); -} - void LogView::clearLine() { QTextCursor cursor(m_ui.view->document()); cursor.setPosition(0); diff --git a/src/platform/qt/LogView.h b/src/platform/qt/LogView.h index 28658672e..d1fe5dbce 100644 --- a/src/platform/qt/LogView.h +++ b/src/platform/qt/LogView.h @@ -16,14 +16,15 @@ extern "C" { namespace QGBA { +class LogController; + class LogView : public QWidget { Q_OBJECT public: - LogView(QWidget* parent = nullptr); + LogView(LogController* log, QWidget* parent = nullptr); signals: - void levelsSet(int levels); void levelsEnabled(int levels); void levelsDisabled(int levels); @@ -32,30 +33,17 @@ public slots: void setLevels(int levels); void clear(); - void setLevelDebug(bool); - void setLevelStub(bool); - void setLevelInfo(bool); - void setLevelWarn(bool); - void setLevelError(bool); - void setLevelFatal(bool); - void setLevelGameError(bool); - void setLevelSWI(bool); - void setLevelStatus(bool); - void setLevelSIO(bool); - +private slots: void setMaxLines(int); private: static const int DEFAULT_LINE_LIMIT = 1000; Ui::LogView m_ui; - int m_logLevel; int m_lines; int m_lineLimit; - static QString toString(int level); - void setLevel(int level); - void clearLevel(int level); + void setLevel(int level, bool); void clearLine(); }; diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index 8c9d964a1..b16018646 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -7,6 +7,7 @@ #include "GBAApp.h" #include "GameController.h" +#include "LogController.h" #include #include @@ -153,7 +154,7 @@ void MemoryModel::save() { } QFile outfile(filename); if (!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - // TODO: Log + LOG(WARN) << tr("Failed to open output file: %1").arg(filename); return; } QDataStream stream(&outfile); @@ -195,9 +196,12 @@ void MemoryModel::paintEvent(QPaintEvent* event) { static QString arg("%0"); QSizeF letterSize = QSizeF(m_letterWidth, m_cellHeight); painter.drawStaticText(QPointF((m_margins.left() - m_regionName.size().width() - 1) / 2.0, 0), m_regionName); - painter.drawText(QRect(QPoint(viewport()->size().width() - m_margins.right(), 0), QSize(m_margins.right(), m_margins.top())), Qt::AlignHCenter, tr("ASCII")); + painter.drawText( + QRect(QPoint(viewport()->size().width() - m_margins.right(), 0), QSize(m_margins.right(), m_margins.top())), + Qt::AlignHCenter, tr("ASCII")); for (int x = 0; x < 16; ++x) { - painter.drawText(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), 0), m_cellSize), Qt::AlignHCenter, QString::number(x, 16).toUpper()); + painter.drawText(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), 0), m_cellSize), Qt::AlignHCenter, + QString::number(x, 16).toUpper()); } int height = (viewport()->size().height() - m_cellHeight) / m_cellHeight; for (int y = 0; y < height; ++y) { @@ -209,38 +213,56 @@ void MemoryModel::paintEvent(QPaintEvent* event) { for (int x = 0; x < 16; x += 2) { uint32_t address = (y + m_top) * 16 + x + m_base; if (isInSelection(address)) { - painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), QSizeF(m_cellSize.width() * 2, m_cellSize.height())), palette.highlight()); + painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), + QSizeF(m_cellSize.width() * 2, m_cellSize.height())), + palette.highlight()); painter.setPen(palette.color(QPalette::HighlightedText)); if (isEditing(address)) { - drawEditingText(painter, QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp)); + drawEditingText( + painter, + QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp)); continue; } } else { painter.setPen(palette.color(QPalette::WindowText)); } uint16_t b = m_cpu->memory.load16(m_cpu, address, nullptr); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp), m_staticNumbers[(b >> 8) & 0xFF]); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 1.0) + m_margins.left(), yp), m_staticNumbers[b & 0xFF]); + painter.drawStaticText( + QPointF(m_cellSize.width() * (x + 1.0) - 2 * m_letterWidth + m_margins.left(), yp), + m_staticNumbers[(b >> 8) & 0xFF]); + painter.drawStaticText(QPointF(m_cellSize.width() * (x + 1.0) + m_margins.left(), yp), + m_staticNumbers[b & 0xFF]); } break; case 4: for (int x = 0; x < 16; x += 4) { uint32_t address = (y + m_top) * 16 + x + m_base; if (isInSelection(address)) { - painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), QSizeF(m_cellSize.width() * 4, m_cellSize.height())), palette.highlight()); + painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), + QSizeF(m_cellSize.width() * 4, m_cellSize.height())), + palette.highlight()); painter.setPen(palette.color(QPalette::HighlightedText)); if (isEditing(address)) { - drawEditingText(painter, QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp)); + drawEditingText( + painter, + QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp)); continue; } } else { painter.setPen(palette.color(QPalette::WindowText)); } uint32_t b = m_cpu->memory.load32(m_cpu, address, nullptr); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp), m_staticNumbers[(b >> 24) & 0xFF]); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) - 2 * m_letterWidth + m_margins.left(), yp), m_staticNumbers[(b >> 16) & 0xFF]); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) + m_margins.left(), yp), m_staticNumbers[(b >> 8) & 0xFF]); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) + 2 * m_letterWidth + m_margins.left(), yp), m_staticNumbers[b & 0xFF]); + painter.drawStaticText( + QPointF(m_cellSize.width() * (x + 2.0) - 4 * m_letterWidth + m_margins.left(), yp), + m_staticNumbers[(b >> 24) & 0xFF]); + painter.drawStaticText( + QPointF(m_cellSize.width() * (x + 2.0) - 2 * m_letterWidth + m_margins.left(), yp), + m_staticNumbers[(b >> 16) & 0xFF]); + painter.drawStaticText(QPointF(m_cellSize.width() * (x + 2.0) + m_margins.left(), yp), + m_staticNumbers[(b >> 8) & 0xFF]); + painter.drawStaticText( + QPointF(m_cellSize.width() * (x + 2.0) + 2 * m_letterWidth + m_margins.left(), yp), + m_staticNumbers[b & 0xFF]); } break; case 1: @@ -248,28 +270,34 @@ void MemoryModel::paintEvent(QPaintEvent* event) { for (int x = 0; x < 16; ++x) { uint32_t address = (y + m_top) * 16 + x + m_base; if (isInSelection(address)) { - painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), m_cellSize), palette.highlight()); + painter.fillRect(QRectF(QPointF(m_cellSize.width() * x + m_margins.left(), yp), m_cellSize), + palette.highlight()); painter.setPen(palette.color(QPalette::HighlightedText)); if (isEditing(address)) { - drawEditingText(painter, QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp)); + drawEditingText(painter, + QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp)); continue; } } else { painter.setPen(palette.color(QPalette::WindowText)); } uint8_t b = m_cpu->memory.load8(m_cpu, address, nullptr); - painter.drawStaticText(QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp), m_staticNumbers[b]); + painter.drawStaticText(QPointF(m_cellSize.width() * (x + 0.5) - m_letterWidth + m_margins.left(), yp), + m_staticNumbers[b]); } break; } painter.setPen(palette.color(QPalette::WindowText)); for (int x = 0; x < 16; ++x) { uint8_t b = m_cpu->memory.load8(m_cpu, (y + m_top) * 16 + x + m_base, nullptr); - painter.drawStaticText(QPointF(viewport()->size().width() - (16 - x) * m_margins.right() / 17.0 - m_letterWidth * 0.5, yp), b < 0x80 ? m_staticAscii[b] : m_staticAscii[0]); + painter.drawStaticText( + QPointF(viewport()->size().width() - (16 - x) * m_margins.right() / 17.0 - m_letterWidth * 0.5, yp), + b < 0x80 ? m_staticAscii[b] : m_staticAscii[0]); } } painter.drawLine(m_margins.left(), 0, m_margins.left(), viewport()->size().height()); - painter.drawLine(viewport()->size().width() - m_margins.right(), 0, viewport()->size().width() - m_margins.right(), viewport()->size().height()); + painter.drawLine(viewport()->size().width() - m_margins.right(), 0, viewport()->size().width() - m_margins.right(), + viewport()->size().height()); painter.drawLine(0, m_margins.top(), viewport()->size().width(), m_margins.top()); } @@ -282,18 +310,28 @@ void MemoryModel::wheelEvent(QWheelEvent* event) { } void MemoryModel::mousePressEvent(QMouseEvent* event) { - if (event->x() < m_margins.left() || event->y() < m_margins.top() || event->x() > size().width() - m_margins.right()) { + if (event->x() < m_margins.left() || event->y() < m_margins.top() || + event->x() > size().width() - m_margins.right()) { m_selection = qMakePair(0, 0); return; } QPoint position(event->pos() - QPoint(m_margins.left(), m_margins.top())); - uint32_t address = int(position.x() / m_cellSize.width()) + (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base; + uint32_t address = int(position.x() / m_cellSize.width()) + + (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base; if (event->button() == Qt::RightButton && isInSelection(address)) { return; } - m_selectionAnchor = address & ~(m_align - 1); - m_selection = qMakePair(m_selectionAnchor, m_selectionAnchor + m_align); + if (event->modifiers() & Qt::ShiftModifier) { + if ((address & ~(m_align - 1)) < m_selectionAnchor) { + m_selection = qMakePair(address & ~(m_align - 1), m_selectionAnchor + m_align); + } else { + m_selection = qMakePair(m_selectionAnchor, (address & ~(m_align - 1)) + m_align); + } + } else { + m_selectionAnchor = address & ~(m_align - 1); + m_selection = qMakePair(m_selectionAnchor, m_selectionAnchor + m_align); + } m_buffer = 0; m_bufferedNybbles = 0; emit selectionChanged(m_selection.first, m_selection.second); @@ -301,12 +339,14 @@ void MemoryModel::mousePressEvent(QMouseEvent* event) { } void MemoryModel::mouseMoveEvent(QMouseEvent* event) { - if (event->x() < m_margins.left() || event->y() < m_margins.top() || event->x() > size().width() - m_margins.right()) { + if (event->x() < m_margins.left() || event->y() < m_margins.top() || + event->x() > size().width() - m_margins.right()) { return; } QPoint position(event->pos() - QPoint(m_margins.left(), m_margins.top())); - uint32_t address = int(position.x() / m_cellSize.width()) + (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base; + uint32_t address = int(position.x() / m_cellSize.width()) + + (int(position.y() / m_cellSize.height()) + m_top) * 16 + m_base; if ((address & ~(m_align - 1)) < m_selectionAnchor) { m_selection = qMakePair(address & ~(m_align - 1), m_selectionAnchor + m_align); } else { @@ -419,7 +459,11 @@ void MemoryModel::drawEditingText(QPainter& painter, const QPointF& origin) { painter.drawStaticText(o, m_staticNumbers[b]); } else { int b = m_buffer & 0xF; - painter.drawStaticText(o, m_staticAscii[b + '0']); + if (b < 10) { + painter.drawStaticText(o, m_staticAscii[b + '0']); + } else { + painter.drawStaticText(o, m_staticAscii[b - 10 + 'A']); + } } o += QPointF(m_letterWidth * 2, 0); } diff --git a/src/platform/qt/MessagePainter.cpp b/src/platform/qt/MessagePainter.cpp index a7dc53710..7aa19a98e 100644 --- a/src/platform/qt/MessagePainter.cpp +++ b/src/platform/qt/MessagePainter.cpp @@ -18,6 +18,7 @@ using namespace QGBA; MessagePainter::MessagePainter(QObject* parent) : QObject(parent) , m_messageTimer(this) + , m_scaleFactor(1) { m_messageFont.setFamily("Source Code Pro"); m_messageFont.setStyleHint(QFont::Monospace); @@ -29,7 +30,7 @@ MessagePainter::MessagePainter(QObject* parent) clearMessage(); } -void MessagePainter::resize(const QSize& size, bool lockAspectRatio) { +void MessagePainter::resize(const QSize& size, bool lockAspectRatio, qreal scaleFactor) { int w = size.width(); int h = size.height(); int drawW = w; @@ -42,36 +43,63 @@ void MessagePainter::resize(const QSize& size, bool lockAspectRatio) { } } m_world.reset(); - m_world.translate((w - drawW) / 2, (h - drawH) / 2); m_world.scale(qreal(drawW) / VIDEO_HORIZONTAL_PIXELS, qreal(drawH) / VIDEO_VERTICAL_PIXELS); + m_scaleFactor = scaleFactor; + m_local = QPoint(1, VIDEO_VERTICAL_PIXELS - m_messageFont.pixelSize() - 1); + m_local = m_world.map(m_local); + m_local += QPoint((w - drawW) / 2, (h - drawH) / 2); + m_pixmapBuffer = QPixmap(drawW * m_scaleFactor, + (m_messageFont.pixelSize() + 2) * m_world.m22() * m_scaleFactor); + m_pixmapBuffer.setDevicePixelRatio(m_scaleFactor); + m_mutex.lock(); m_message.prepare(m_world, m_messageFont); + redraw(); + m_mutex.unlock(); +} + +void MessagePainter::redraw() { + m_pixmapBuffer.fill(Qt::transparent); + if (m_message.text().isEmpty()) { + m_pixmap = m_pixmapBuffer; + m_pixmap.setDevicePixelRatio(m_scaleFactor); + return; + } + QPainter painter(&m_pixmapBuffer); + painter.setWorldTransform(m_world); + painter.setRenderHint(QPainter::Antialiasing); + painter.setFont(m_messageFont); + painter.setPen(Qt::black); + const static int ITERATIONS = 11; + for (int i = 0; i < ITERATIONS; ++i) { + painter.save(); + painter.translate(cos(i * 2.0 * M_PI / ITERATIONS) * 0.8, sin(i * 2.0 * M_PI / ITERATIONS) * 0.8); + painter.drawStaticText(0, 0, m_message); + painter.restore(); + } + painter.setPen(Qt::white); + painter.drawStaticText(0, 0, m_message); + m_pixmap = m_pixmapBuffer; + m_pixmap.setDevicePixelRatio(m_scaleFactor); } void MessagePainter::paint(QPainter* painter) { - painter->setWorldTransform(m_world); - painter->setRenderHint(QPainter::Antialiasing); - painter->setFont(m_messageFont); - painter->setPen(Qt::black); - painter->translate(1, VIDEO_VERTICAL_PIXELS - m_messageFont.pixelSize() - 1); - const static int ITERATIONS = 11; - for (int i = 0; i < ITERATIONS; ++i) { - painter->save(); - painter->translate(cos(i * 2.0 * M_PI / ITERATIONS) * 0.8, sin(i * 2.0 * M_PI / ITERATIONS) * 0.8); - painter->drawStaticText(0, 0, m_message); - painter->restore(); - } - painter->setPen(Qt::white); - painter->drawStaticText(0, 0, m_message); + painter->drawPixmap(m_local, m_pixmap); } + void MessagePainter::showMessage(const QString& message) { + m_mutex.lock(); m_message.setText(message); - m_message.prepare(m_world, m_messageFont); + redraw(); + m_mutex.unlock(); m_messageTimer.stop(); m_messageTimer.start(); } void MessagePainter::clearMessage() { + m_mutex.lock(); m_message.setText(QString()); + redraw(); + m_mutex.unlock(); m_messageTimer.stop(); } diff --git a/src/platform/qt/MessagePainter.h b/src/platform/qt/MessagePainter.h index d0be66bbd..7e0bff53b 100644 --- a/src/platform/qt/MessagePainter.h +++ b/src/platform/qt/MessagePainter.h @@ -6,7 +6,9 @@ #ifndef QGBA_MESSAGE_PAINTER #define QGBA_MESSAGE_PAINTER +#include #include +#include #include #include @@ -18,18 +20,26 @@ Q_OBJECT public: MessagePainter(QObject* parent = nullptr); - void resize(const QSize& size, bool lockAspectRatio); + void resize(const QSize& size, bool lockAspectRatio, qreal scaleFactor); void paint(QPainter* painter); + void setScaleFactor(qreal factor); public slots: void showMessage(const QString& message); void clearMessage(); private: + void redraw(); + + QMutex m_mutex; QStaticText m_message; + QPixmap m_pixmap; + QPixmap m_pixmapBuffer; QTimer m_messageTimer; + QPoint m_local; QTransform m_world; QFont m_messageFont; + qreal m_scaleFactor; }; } diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 7c2b0495c..76a6ca35a 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -87,7 +87,8 @@ void OverrideView::updateOverrides() { m_override.idleLoop = parsedIdleLoop; } - if (m_override.savetype != SAVEDATA_AUTODETECT || m_override.hardware != HW_NO_OVERRIDE || m_override.idleLoop != IDLE_LOOP_NONE) { + if (m_override.savetype != SAVEDATA_AUTODETECT || m_override.hardware != HW_NO_OVERRIDE || + m_override.idleLoop != IDLE_LOOP_NONE) { m_controller->setOverride(m_override); } else { m_controller->clearOverride(); @@ -119,7 +120,6 @@ void OverrideView::gameStarted(GBAThread* thread) { m_ui.idleLoop->setText(QString::number(thread->gba->idleLoop, 16)); } else { m_ui.idleLoop->clear(); - } GBAGetGameCode(thread->gba, m_override.id); diff --git a/src/platform/qt/PaletteView.cpp b/src/platform/qt/PaletteView.cpp index 15f338438..039faef2f 100644 --- a/src/platform/qt/PaletteView.cpp +++ b/src/platform/qt/PaletteView.cpp @@ -6,6 +6,7 @@ #include "PaletteView.h" #include "GBAApp.h" +#include "LogController.h" #include "VFileDevice.h" #include @@ -84,7 +85,8 @@ void PaletteView::exportPalette(int start, int length) { length = 512 - start; } m_controller->threadInterrupt(); - QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export palette"), tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); + QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export palette"), + tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); if (!dialog->exec()) { m_controller->threadContinue(); return; @@ -92,6 +94,7 @@ void PaletteView::exportPalette(int start, int length) { QString filename = dialog->selectedFiles()[0]; VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { + LOG(ERROR) << tr("Failed to open output palette file: %1").arg(filename); m_controller->threadContinue(); return; } diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index e1ce46a92..501b05fd9 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -91,7 +91,8 @@ bool SensorView::eventFilter(QObject*, QEvent* event) { void SensorView::updateSensors() { m_controller->threadInterrupt(); - if (m_rotation->sample && (!m_controller->isLoaded() || !(m_controller->thread()->gba->memory.hw.devices & (HW_GYRO | HW_TILT)))) { + if (m_rotation->sample && + (!m_controller->isLoaded() || !(m_controller->thread()->gba->memory.hw.devices & (HW_GYRO | HW_TILT)))) { m_rotation->sample(m_rotation); m_rotation->sample(m_rotation); m_rotation->sample(m_rotation); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index b8fa8e9cf..5f288da37 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -7,6 +7,7 @@ #include "AudioProcessor.h" #include "ConfigController.h" +#include "Display.h" #include "GBAApp.h" using namespace QGBA; @@ -59,6 +60,19 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent) } #endif + QVariant displayDriver = m_controller->getQtOption("displayDriver"); + m_ui.displayDriver->addItem(tr("Software (Qt)"), static_cast(Display::Driver::QT)); + if (!displayDriver.isNull() && displayDriver.toInt() == static_cast(Display::Driver::QT)) { + m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1); + } + +#ifdef BUILD_GL + m_ui.displayDriver->addItem(tr("OpenGL"), static_cast(Display::Driver::OPENGL)); + if (displayDriver.isNull() || displayDriver.toInt() == static_cast(Display::Driver::OPENGL)) { + m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1); + } +#endif + connect(m_ui.biosBrowse, SIGNAL(clicked()), this, SLOT(selectBios())); connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig())); } @@ -108,6 +122,13 @@ void SettingsView::updateConfig() { emit audioDriverChanged(); } + QVariant displayDriver = m_ui.displayDriver->itemData(m_ui.displayDriver->currentIndex()); + if (displayDriver != m_controller->getQtOption("displayDriver")) { + m_controller->setQtOption("displayDriver", displayDriver); + Display::setDriver(static_cast(displayDriver.toInt())); + emit displayDriverChanged(); + } + m_controller->write(); emit biosLoaded(m_ui.bios->text()); diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index bf480272e..782e20e63 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -23,6 +23,7 @@ public: signals: void biosLoaded(const QString&); void audioDriverChanged(); + void displayDriverChanged(); private slots: void selectBios(); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 077ebf807..9de1f76e4 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -6,7 +6,7 @@ 0 0 - 673 + 698 366 @@ -140,30 +140,12 @@ - + - Sync: + Display driver: - - - - - - Video - - - - - - - Audio - - - - - @@ -230,27 +212,52 @@ + + + + Sync: + + + + + + + + Video + + + + + + + Audio + + + + + + Lock aspect ratio - + Resample video - - - - Suspend screensaver - - - true + + + + + 0 + 0 + @@ -387,14 +394,24 @@ - + + + + Suspend screensaver + + + true + + + + Idle loops - + diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 897f99ad1..34993a386 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -118,10 +118,12 @@ void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& if (m_config) { loadShortcuts(item); } - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); + emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), + createIndex(smenu->items().count() - 1, 2, item)); } -void ShortcutController::addFunctions(QMenu* menu, std::function press, std::function release, const QKeySequence& shortcut, const QString& visibleName, const QString& name) { +void ShortcutController::addFunctions(QMenu* menu, std::function press, std::function release, + const QKeySequence& shortcut, const QString& visibleName, const QString& name) { ShortcutItem* smenu = m_menuMap[menu]; if (!smenu) { return; @@ -137,7 +139,8 @@ void ShortcutController::addFunctions(QMenu* menu, std::function press, loadShortcuts(item); } m_heldKeys[shortcut] = item; - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); + emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), + createIndex(smenu->items().count() - 1, 2, item)); } void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) { @@ -155,7 +158,8 @@ void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) { smenu->addSubmenu(menu); endInsertRows(); ShortcutItem* item = &smenu->items().last(); - emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); + emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), + createIndex(smenu->items().count() - 1, 2, item)); m_menuMap[menu] = item; } @@ -211,7 +215,8 @@ void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& if (m_config) { m_config->setQtOption(item->name(), keySequence.toString(), KEY_SECTION); } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); + emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), + createIndex(index.row(), 2, index.internalPointer())); } void ShortcutController::updateButton(const QModelIndex& index, int button) { @@ -235,7 +240,8 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) { if (m_config) { m_config->setQtOption(item->name(), button, BUTTON_SECTION); } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); + emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), + createIndex(index.row(), 2, index.internalPointer())); } void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) { @@ -267,7 +273,8 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA } m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_SECTION); } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); + emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), + createIndex(index.row(), 2, index.internalPointer())); } void ShortcutController::clearKey(const QModelIndex& index) { @@ -473,7 +480,9 @@ void ShortcutController::ShortcutItem::addAction(QAction* action, const QString& m_items.append(ShortcutItem(action, name, this)); } -void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name) { +void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, + const QKeySequence& shortcut, const QString& visibleName, + const QString& name) { m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this)); } diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index 48693aeb9..f8be878bf 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -35,7 +35,8 @@ private: typedef QPair, std::function> Functions; ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr); - ShortcutItem(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent = nullptr); + ShortcutItem(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, + ShortcutItem* parent = nullptr); ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr); QAction* action() { return m_action; } @@ -51,7 +52,8 @@ private: ShortcutItem* parent() { return m_parent; } const ShortcutItem* parent() const { return m_parent; } void addAction(QAction* action, const QString& name); - void addFunctions(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name); + void addFunctions(Functions functions, const QKeySequence& shortcut, const QString& visibleName, + const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } void setShortcut(const QKeySequence& sequence); @@ -60,7 +62,9 @@ private: GamepadAxisEvent::Direction direction() const { return m_direction; } void setAxis(int axis, GamepadAxisEvent::Direction direction); - bool operator==(const ShortcutItem& other) const { return m_menu == other.m_menu && m_action == other.m_action; } + bool operator==(const ShortcutItem& other) const { + return m_menu == other.m_menu && m_action == other.m_action; + } private: QAction* m_action; @@ -91,7 +95,8 @@ public: virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; void addAction(QMenu* menu, QAction* action, const QString& name); - void addFunctions(QMenu* menu, std::function press, std::function release, const QKeySequence& shortcut, const QString& visibleName, const QString& name); + void addFunctions(QMenu* menu, std::function press, std::function release, + const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); QKeySequence shortcutAt(const QModelIndex& index) const; diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index c1c6a25d3..b0ba9045f 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -104,10 +104,10 @@ void ShortcutView::updateButton(int button) { m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); } - void ShortcutView::updateAxis(int axis, int direction) { if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { return; } - m_controller->updateAxis(m_ui.shortcutTable->selectionModel()->currentIndex(), axis, static_cast(direction)); + m_controller->updateAxis(m_ui.shortcutTable->selectionModel()->currentIndex(), axis, + static_cast(direction)); } diff --git a/src/platform/qt/VFileDevice.cpp b/src/platform/qt/VFileDevice.cpp index 1e44af382..7e6a006fe 100644 --- a/src/platform/qt/VFileDevice.cpp +++ b/src/platform/qt/VFileDevice.cpp @@ -27,5 +27,5 @@ qint64 VFileDevice::size() const { } VFile* VFileDevice::open(QString path, int mode) { - return VFileOpen(path.toLocal8Bit().constData(), mode); + return VFileOpen(path.toUtf8().constData(), mode); } diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index d8f6fb65f..b6b127c61 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -8,6 +8,7 @@ #ifdef USE_FFMPEG #include "GBAApp.h" +#include "LogController.h" #include @@ -131,8 +132,8 @@ VideoView::VideoView(QWidget* parent) .acodec = QString(), .vbr = 0, .abr = 0, - .width = 240, - .height = 160 + .width = VIDEO_HORIZONTAL_PIXELS, + .height = VIDEO_VERTICAL_PIXELS }); addPreset(m_ui.presetHQ, (Preset) { @@ -169,8 +170,8 @@ VideoView::VideoView(QWidget* parent) .acodec = "FLAC", .vbr = 0, .abr = 0, - .width = 240, - .height = 160, + .width = VIDEO_HORIZONTAL_PIXELS, + .height = VIDEO_VERTICAL_PIXELS, }); setPreset((Preset) { @@ -179,8 +180,8 @@ VideoView::VideoView(QWidget* parent) .acodec = "FLAC", .vbr = 0, .abr = 0, - .width = 240, - .height = 160, + .width = VIDEO_HORIZONTAL_PIXELS, + .height = VIDEO_VERTICAL_PIXELS, }); showAdvanced(false); @@ -197,7 +198,8 @@ void VideoView::startRecording() { if (!validateSettings()) { return; } - if (!FFmpegEncoderOpen(&m_encoder, m_filename.toLocal8Bit().constData())) { + if (!FFmpegEncoderOpen(&m_encoder, m_filename.toUtf8().constData())) { + LOG(ERROR) << tr("Failed to open output video file: %1").arg(m_filename); return; } m_ui.start->setEnabled(false); @@ -230,7 +232,7 @@ void VideoView::setAudioCodec(const QString& codec, bool manual) { if (m_audioCodec == "none") { m_audioCodecCstr = nullptr; } else { - m_audioCodecCstr = strdup(m_audioCodec.toLocal8Bit().constData()); + m_audioCodecCstr = strdup(m_audioCodec.toUtf8().constData()); } if (!FFmpegEncoderSetAudio(&m_encoder, m_audioCodecCstr, m_abr)) { free(m_audioCodecCstr); @@ -246,7 +248,7 @@ void VideoView::setAudioCodec(const QString& codec, bool manual) { void VideoView::setVideoCodec(const QString& codec, bool manual) { free(m_videoCodecCstr); m_videoCodec = sanitizeCodec(codec, s_vcodecMap); - m_videoCodecCstr = strdup(m_videoCodec.toLocal8Bit().constData()); + m_videoCodecCstr = strdup(m_videoCodec.toUtf8().constData()); if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) { free(m_videoCodecCstr); m_videoCodecCstr = nullptr; @@ -261,7 +263,7 @@ void VideoView::setVideoCodec(const QString& codec, bool manual) { void VideoView::setContainer(const QString& container, bool manual) { free(m_containerCstr); m_container = sanitizeCodec(container, s_containerMap); - m_containerCstr = strdup(m_container.toLocal8Bit().constData()); + m_containerCstr = strdup(m_container.toUtf8().constData()); if (!FFmpegEncoderSetContainer(&m_encoder, m_containerCstr)) { free(m_containerCstr); m_containerCstr = nullptr; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index d7e5c6662..2feb07d6d 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -47,7 +47,8 @@ using std::isnan; Window::Window(ConfigController* config, int playerId, QWidget* parent) : QMainWindow(parent) - , m_logView(new LogView()) + , m_log(0) + , m_logView(new LogView(&m_log)) , m_stateWindow(nullptr) , m_screenWidget(new WindowBackground()) , m_logo(":/res/mgba-1024.png") @@ -110,16 +111,16 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(m_controller, SIGNAL(gamePaused(GBAThread*)), &m_inputController, SLOT(resumeScreensaver())); connect(m_controller, SIGNAL(gameUnpaused(GBAThread*)), m_display, SLOT(unpauseDrawing())); connect(m_controller, SIGNAL(gameUnpaused(GBAThread*)), &m_inputController, SLOT(suspendScreensaver())); - connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&))); + connect(m_controller, SIGNAL(postLog(int, const QString&)), &m_log, SLOT(postLog(int, const QString&))); connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame())); connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), m_display, SLOT(framePosted(const uint32_t*))); connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&))); connect(m_controller, SIGNAL(gameFailed()), this, SLOT(gameFailed())); connect(m_controller, SIGNAL(unimplementedBiosCall(int)), this, SLOT(unimplementedBiosCall(int))); connect(m_controller, SIGNAL(statusPosted(const QString&)), m_display, SLOT(showMessage(const QString&))); - connect(m_logView, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int))); - connect(m_logView, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int))); - connect(m_logView, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int))); + connect(&m_log, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int))); + connect(&m_log, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int))); + connect(&m_log, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int))); connect(this, SIGNAL(startDrawing(GBAThread*)), m_display, SLOT(startDrawing(GBAThread*)), Qt::QueuedConnection); connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing())); connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame())); @@ -128,7 +129,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); - m_logView->setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS); + m_log.setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); m_shortcutController->setConfigController(m_config); @@ -173,7 +174,7 @@ void Window::setConfig(ConfigController* config) { void Window::loadConfig() { const GBAOptions* opts = m_config->options(); - m_logView->setLevels(opts->logLevel); + m_log.setLevels(opts->logLevel); m_controller->setOptions(opts); m_display->lockAspectRatio(opts->lockAspectRatio); @@ -476,8 +477,8 @@ void Window::exitFullScreen() { return; } unsetCursor(); - showNormal(); menuBar()->show(); + showNormal(); } void Window::toggleFullScreen() { @@ -509,7 +510,7 @@ void Window::gameStarted(GBAThread* context) { attachWidget(m_display); #ifndef Q_OS_MAC - if(isFullScreen()) { + if (isFullScreen()) { menuBar()->hide(); } #endif @@ -532,16 +533,16 @@ void Window::gameStopped() { void Window::gameCrashed(const QString& errorMessage) { QMessageBox* crash = new QMessageBox(QMessageBox::Critical, tr("Crash"), - tr("The game has crashed with the following error:\n\n%1").arg(errorMessage), - QMessageBox::Ok, this, Qt::Sheet); + tr("The game has crashed with the following error:\n\n%1").arg(errorMessage), + QMessageBox::Ok, this, Qt::Sheet); crash->setAttribute(Qt::WA_DeleteOnClose); crash->show(); } void Window::gameFailed() { QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Load"), - tr("Could not load game. Are you sure it's in the correct format?"), - QMessageBox::Ok, this, Qt::Sheet); + tr("Could not load game. Are you sure it's in the correct format?"), + QMessageBox::Ok, this, Qt::Sheet); fail->setAttribute(Qt::WA_DeleteOnClose); fail->show(); } @@ -552,9 +553,10 @@ void Window::unimplementedBiosCall(int call) { } m_hitUnimplementedBiosCall = true; - QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Unimplemented BIOS call"), - tr("This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience."), - QMessageBox::Ok, this, Qt::Sheet); + QMessageBox* fail = new QMessageBox( + QMessageBox::Warning, tr("Unimplemented BIOS call"), + tr("This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience."), + QMessageBox::Ok, this, Qt::Sheet); fail->setAttribute(Qt::WA_DeleteOnClose); fail->show(); } @@ -628,7 +630,8 @@ void Window::setupMenu(QMenuBar* menubar) { QMenu* fileMenu = menubar->addMenu(tr("&File")); m_shortcutController->addMenu(fileMenu); installEventFilter(m_shortcutController); - addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM"); + addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), + "loadROM"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch"); addControlledAction(fileMenu, fileMenu->addAction(tr("Boot BIOS"), m_controller, SLOT(bootBIOS())), "bootBIOS"); @@ -733,7 +736,8 @@ void Window::setupMenu(QMenuBar* menubar) { connect(m_controller, &GameController::gamePaused, [this, pause]() { pause->setChecked(true); - QImage currentImage(reinterpret_cast(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32); + QImage currentImage(reinterpret_cast(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, + VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32); QPixmap pixmap; pixmap.convertFromImage(currentImage.rgbSwapped()); m_screenWidget->setPixmap(pixmap); @@ -854,7 +858,7 @@ void Window::setupMenu(QMenuBar* menubar) { } QKeySequence fullscreenKeys; #ifdef Q_OS_WIN - fullscreenKeys = QKeySequence("Alt+Enter"); + fullscreenKeys = QKeySequence("Alt+Return"); #else fullscreenKeys = QKeySequence("Ctrl+F"); #endif @@ -1010,8 +1014,10 @@ void Window::setupMenu(QMenuBar* menubar) { #endif toolsMenu->addSeparator(); - addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())), "settings"); - addControlledAction(toolsMenu, toolsMenu->addAction(tr("Edit shortcuts..."), this, SLOT(openShortcutWindow())), "shortcuts"); + addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())), + "settings"); + addControlledAction(toolsMenu, toolsMenu->addAction(tr("Edit shortcuts..."), this, SLOT(openShortcutWindow())), + "shortcuts"); QAction* keymap = new QAction(tr("Remap keyboard..."), toolsMenu); connect(keymap, SIGNAL(triggered()), this, SLOT(openKeymapWindow())); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index a7536322c..f0377c185 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -20,6 +20,7 @@ extern "C" { #include "GDBController.h" #include "InputController.h" #include "LoadSaveState.h" +#include "LogController.h" struct GBAOptions; struct GBAArguments; @@ -140,6 +141,7 @@ private: GameController* m_controller; Display* m_display; QList m_gameActions; + LogController m_log; LogView* m_logView; LoadSaveState* m_stateWindow; WindowBackground* m_screenWidget; diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 8fd001df2..8152cf3a2 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -9,12 +9,12 @@ #ifdef QT_STATIC #include #ifdef _WIN32 -Q_IMPORT_PLUGIN (QWindowsIntegrationPlugin); -Q_IMPORT_PLUGIN (QWindowsAudioPlugin); +Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +Q_IMPORT_PLUGIN(QWindowsAudioPlugin); #endif #endif int main(int argc, char* argv[]) { - QGBA::GBAApp application(argc, argv); - return application.exec(); + QGBA::GBAApp application(argc, argv); + return application.exec(); } diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 94ba32492..88510b37b 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -41,7 +41,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsdl${SDL_VE file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) set(PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY} ${PIXMAN-1_LIBRARIES}) -include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl ${PIXMAN-1_INCLUDE_DIR} ${SDL_INCLUDE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl ${PIXMAN-1_INCLUDE_DIRS} ${SDL_INCLUDE_DIR}) set(SDL_INCLUDE_DIR "${SDL_INCLUDE_DIR}" PARENT_SCOPE) set(SDL_LIBRARY "${SDL_LIBRARY}" PARENT_SCOPE) diff --git a/src/platform/sdl/gl-sdl.c b/src/platform/sdl/gl-sdl.c index 6b9c7f4c5..429d67a65 100644 --- a/src/platform/sdl/gl-sdl.c +++ b/src/platform/sdl/gl-sdl.c @@ -51,7 +51,7 @@ bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) { #if SDL_VERSION_ATLEAST(2, 0, 0) renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen)); - SDL_GL_CreateContext(renderer->window); + renderer->glCtx = SDL_GL_CreateContext(renderer->window); SDL_GL_SetSwapInterval(1); SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); renderer->player.window = renderer->window; @@ -109,4 +109,7 @@ void GBASDLGLDeinit(struct SDLSoftwareRenderer* renderer) { renderer->gl.d.deinit(&renderer->gl.d); } free(renderer->d.outputBuffer); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GL_DeleteContext(renderer->glCtx); +#endif } diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 5124d6754..0b3143eeb 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -176,5 +176,4 @@ static void GBASDLDeinit(struct SDLSoftwareRenderer* renderer) { renderer->deinit(renderer); SDL_Quit(); - } diff --git a/src/platform/sdl/main.h b/src/platform/sdl/main.h index 55082078d..4a428c190 100644 --- a/src/platform/sdl/main.h +++ b/src/platform/sdl/main.h @@ -45,6 +45,7 @@ struct SDLSoftwareRenderer { SDL_Window* window; SDL_Texture* sdlTex; SDL_Renderer* sdlRenderer; + SDL_GLContext* glCtx; #endif int viewportWidth; diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index fea425f41..6e0fdebb2 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -76,7 +76,6 @@ void GBASDLPauseAudio(struct GBASDLAudio* context) { UNUSED(context); SDL_PauseAudio(1); #endif - } void GBASDLResumeAudio(struct GBASDLAudio* context) { diff --git a/src/platform/sdl/sw-sdl.c b/src/platform/sdl/sw-sdl.c index ee295e846..b7090f61d 100644 --- a/src/platform/sdl/sw-sdl.c +++ b/src/platform/sdl/sw-sdl.c @@ -65,7 +65,7 @@ bool GBASDLSWInit(struct SDLSoftwareRenderer* renderer) { pixman_format_code_t format = PIXMAN_x8b8g8r8; #endif renderer->pix = pixman_image_create_bits(format, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, - renderer->d.outputBuffer, renderer->d.outputBufferStride * BYTES_PER_PIXEL); + renderer->d.outputBuffer, renderer->d.outputBufferStride * BYTES_PER_PIXEL); renderer->screenpix = pixman_image_create_bits(format, renderer->viewportWidth, renderer->viewportHeight, surface->pixels, surface->pitch); pixman_transform_t transform; @@ -104,17 +104,17 @@ void GBASDLSWRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* rend #ifdef USE_PIXMAN if (renderer->ratio > 1) { pixman_image_composite32(PIXMAN_OP_SRC, renderer->pix, 0, renderer->screenpix, - 0, 0, 0, 0, 0, 0, - renderer->viewportWidth, renderer->viewportHeight); + 0, 0, 0, 0, 0, 0, + renderer->viewportWidth, renderer->viewportHeight); } #else switch (renderer->ratio) { #if defined(__ARM_NEON) && COLOR_16_BIT case 2: - _neon2x(surface->pixels, renderer->d.outputBuffer, 240, 160); + _neon2x(surface->pixels, renderer->d.outputBuffer, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); break; case 4: - _neon4x(surface->pixels, renderer->d.outputBuffer, 240, 160); + _neon4x(surface->pixels, renderer->d.outputBuffer, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); break; #endif case 1: diff --git a/src/util/common.h b/src/util/common.h index d32d6877f..be005702d 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -6,7 +6,9 @@ #ifndef COMMON_H #define COMMON_H +#ifndef PSP2 #include +#endif #include #include #include @@ -22,12 +24,14 @@ #include "version.h" #ifdef _MSC_VER -typedef intptr_t off_t; +#include typedef intptr_t ssize_t; +#define inline __inline #define restrict __restrict #define strcasecmp _stricmp #define strncasecmp _strnicmp #define ftruncate _chsize +#define snprintf _snprintf #elif defined(__wii__) typedef intptr_t ssize_t; #else diff --git a/src/util/patch-ips.c b/src/util/patch-ips.c index 668f95795..6d6d53be9 100644 --- a/src/util/patch-ips.c +++ b/src/util/patch-ips.c @@ -39,7 +39,8 @@ bool loadPatchIPS(struct Patch* patch) { size_t _IPSOutputSize(struct Patch* patch, size_t inSize) { UNUSED(patch); - return inSize; + UNUSED(inSize); + return 16 * 1024 * 1024; // IPS patches can grow up to 16MiB, but not beyond } bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) { diff --git a/src/util/socket.h b/src/util/socket.h index 3a4712052..e78b95fdf 100644 --- a/src/util/socket.h +++ b/src/util/socket.h @@ -110,7 +110,6 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) bindInfo.sin6_port = htons(port); memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr)); err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); - } if (err) { close(sock); diff --git a/src/util/string.h b/src/util/string.h index c49bc06d0..12f088741 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -8,12 +8,12 @@ #include "util/common.h" -#ifndef strndup +#ifndef HAVE_STRNDUP // This is sometimes a macro char* strndup(const char* start, size_t len); #endif -#ifndef strdup +#ifndef HAVE_STRDUP char* strdup(const char* str); #endif diff --git a/src/util/vfs.c b/src/util/vfs.c index 4cf207e34..ab776e4f3 100644 --- a/src/util/vfs.c +++ b/src/util/vfs.c @@ -7,6 +7,8 @@ #ifdef PSP #include "platform/psp/sce-vfs.h" +#elif defined(PSP2) +#include "platform/psp2/sce-vfs.h" #endif struct VFile* VFileOpen(const char* path, int flags) { @@ -34,7 +36,7 @@ struct VFile* VFileOpen(const char* path, int flags) { break; } return VFileFOpen(path, chflags); -#elif defined(PSP) +#elif defined(PSP) || defined(PSP2) return VFileOpenSce(path, flags, 0666); #else return VFileOpenFD(path, flags); @@ -42,7 +44,7 @@ struct VFile* VFileOpen(const char* path, int flags) { } ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size) { - ssize_t bytesRead = 0; + size_t bytesRead = 0; while (bytesRead < size - 1) { ssize_t newRead = vf->read(vf, &buffer[bytesRead], 1); if (newRead <= 0) { diff --git a/src/util/vfs.h b/src/util/vfs.h index 6b43d29d0..3f74d721a 100644 --- a/src/util/vfs.h +++ b/src/util/vfs.h @@ -70,8 +70,10 @@ struct VDir* VDirOpenZip(const char* path, int flags); struct VDir* VDirOpen7z(const char* path, int flags); #endif -struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode); -struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode); +struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, + int mode); +struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, + const char* infix, const char* suffix, int mode); ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size); diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index 8e7196028..383d6bbff 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -9,6 +9,8 @@ #include #ifndef _WIN32 #include +#else +#include #endif struct VFileFD { @@ -35,8 +37,12 @@ struct VFile* VFileOpenFD(const char* path, int flags) { } #ifdef _WIN32 flags |= O_BINARY; -#endif + wchar_t wpath[PATH_MAX]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath) / sizeof(*wpath)); + int fd = _wopen(wpath, flags, 0666); +#else int fd = open(path, flags, 0666); +#endif return VFileFromFD(fd); } diff --git a/src/util/vfs/vfs-file.c b/src/util/vfs/vfs-file.c index a995aeccf..70b676e63 100644 --- a/src/util/vfs/vfs-file.c +++ b/src/util/vfs/vfs-file.c @@ -13,6 +13,7 @@ struct VFileFILE { struct VFile d; FILE* file; + bool writable; }; static bool _vffClose(struct VFile* vf); @@ -46,6 +47,7 @@ struct VFile* VFileFromFILE(FILE* file) { } vff->file = file; + vff->writable = false; vff->d.close = _vffClose; vff->d.seek = _vffSeek; vff->d.read = _vffRead; @@ -84,8 +86,10 @@ ssize_t _vffWrite(struct VFile* vf, const void* buffer, size_t size) { } static void* _vffMap(struct VFile* vf, size_t size, int flags) { - UNUSED(flags); struct VFileFILE* vff = (struct VFileFILE*) vf; + if (flags & MAP_WRITE) { + vff->writable = true; + } void* mem = anonymousMemoryMap(size); if (!mem) { return 0; @@ -99,10 +103,12 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags) { static void _vffUnmap(struct VFile* vf, void* memory, size_t size) { struct VFileFILE* vff = (struct VFileFILE*) vf; - long pos = ftell(vff->file); - fseek(vff->file, 0, SEEK_SET); - fwrite(memory, size, 1, vff->file); - fseek(vff->file, pos, SEEK_SET); + if (vff->writable) { + long pos = ftell(vff->file); + fseek(vff->file, 0, SEEK_SET); + fwrite(memory, size, 1, vff->file); + fseek(vff->file, pos, SEEK_SET); + } mappedMemoryFree(memory, size); }