Merge branch 'port/psp2' into port/crucible

This commit is contained in:
Jeffrey Pfau 2015-07-04 21:48:47 -07:00
commit 03842fb95a
114 changed files with 1782 additions and 1035 deletions

34
.clang-format Normal file
View File

@ -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

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/build
*.user*
*~
*.swp

15
CHANGES
View File

@ -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:

View File

@ -1,7 +1,11 @@
cmake_minimum_required(VERSION 2.6)
project(mGBA C)
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
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})

View File

@ -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[<sup>[2]</sup>](#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.

View File

@ -42,7 +42,6 @@ void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
cpu->bankedSPSRs[oldBank] = cpu->spsr.packed;
cpu->spsr.packed = cpu->bankedSPSRs[newBank];
}
cpu->privilegeMode = mode;
}
@ -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) {

View File

@ -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);
};

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)), \

View File

@ -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;
})

View File

@ -15,5 +15,4 @@ struct ARMCore;
typedef void (*ARMInstruction)(struct ARMCore*, uint32_t opcode);
const ARMInstruction _armTable[0x1000];
#endif

View File

@ -36,14 +36,18 @@
#define ARM_V_SUBTRACTION(M, N, D) ((ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
#define ARM_WAIT_MUL(R) \
{ \
int32_t wait; \
if ((R & 0xFFFFFF00) == 0xFFFFFF00 || !(R & 0xFFFFFF00)) { \
currentCycles += 1; \
wait = 1; \
} else if ((R & 0xFFFF0000) == 0xFFFF0000 || !(R & 0xFFFF0000)) { \
currentCycles += 2; \
wait = 2; \
} else if ((R & 0xFF000000) == 0xFF000000 || !(R & 0xFF000000)) { \
currentCycles += 3; \
wait = 3; \
} else { \
currentCycles += 4; \
wait = 4; \
} \
currentCycles += cpu->memory.stall(cpu, wait); \
}
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
@ -55,7 +59,7 @@
LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
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); \
@ -63,7 +67,7 @@
LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
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;

View File

@ -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], &currentCycles); THUMB_STORE_POST_BODY;)
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], &currentCycles); 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, &currentCycles); THUMB_LOAD_POST_BODY;)
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(LDR4, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[ARM_SP] + immediate, &currentCycles); THUMB_LOAD_POST_BODY;)
DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(STR3, cpu->memory.store32(cpu, cpu->gprs[ARM_SP] + immediate, cpu->gprs[rd], &currentCycles); 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], &currentCycles); THUMB_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); THUMB_LOAD_POST_BODY;)
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], &currentCycles); 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], &currentCycles); THUMB_STORE_POST_BODY;)
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], &currentCycles); 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, &currentCycles); \
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,

View File

@ -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) \

View File

@ -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);

View File

@ -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;

View File

@ -457,7 +457,6 @@ static void _unHuffman(struct GBA* gba) {
block = 0;
}
}
}
cpu->gprs[0] = source;
cpu->gprs[1] = dest;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -6,6 +6,11 @@
#include "hardware.h"
#include "gba/serialize.h"
#include "util/hash.h"
#ifdef PSP2
#include <psp2/rtc.h>
#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) {
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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}
@ -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;
}
@ -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);

View File

@ -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);

View File

@ -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,14 +63,16 @@ 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;
@ -123,24 +130,29 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
color_t* objwinPalette = renderer->normalPalette; \
UNUSED(objwinPalette); \
if (objwinSlowPath) { \
if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
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); \
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); \
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); \
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); \
objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && \
GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
break; \
} \
@ -163,14 +175,17 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
\
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)); \
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)); \
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); \
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; \

View File

@ -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);
if (success) {
gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels);
GBASyncForceFrame(gba->sync);
}
free(pixels);
return true;
return success;
}
#endif

View File

@ -149,7 +149,6 @@ cleanup:
return false;
}
bool GBASavedataExportSharkPort(const struct GBA* gba, struct VFile* vf) {
union {
char c[0x1C];

View File

@ -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;

View File

@ -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

View File

@ -13,6 +13,7 @@
#ifdef _WIN32
#include <windows.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <strsafe.h>
#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
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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

135
src/platform/psp2/main.c Normal file
View File

@ -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 <psp2/ctrl.h>
#include <psp2/display.h>
#include <psp2/gxm.h>
#include <psp2/moduleinfo.h>
#include <psp2/kernel/memorymgr.h>
#include <psp2/kernel/processmgr.h>
#include <vita2d.h>
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;
}

View File

@ -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 <psp2/kernel/memorymgr.h>
#include <psp2/types.h>
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;
}
}
}

View File

@ -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;
}

View File

@ -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 <psp2/types.h>
#include <psp2/io/fcntl.h>
#else
#include <pspiofilemgr.h>
#endif
struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode);
#endif

View File

@ -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;
}

View File

@ -6,6 +6,7 @@
#include "AudioProcessorQt.h"
#include "AudioDevice.h"
#include "LogController.h"
#include <QAudioOutput>
@ -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;
}

View File

@ -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;
}

View File

@ -63,6 +63,7 @@ set(SOURCE_FILES
InputController.cpp
KeyEditor.cpp
LoadSaveState.cpp
LogController.cpp
LogView.cpp
MemoryModel.cpp
MemoryView.cpp

View File

@ -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 <QFont>
@ -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:
@ -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();

View File

@ -112,7 +112,7 @@ void CheatsView::enterCheat(std::function<bool(GBACheatSet*, const char*)> 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();

View File

@ -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) {

View File

@ -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);
}

View File

@ -8,6 +8,8 @@
#include <QWidget>
#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;
};
}

View File

@ -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<WHandle>(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();
if (m_messagePainter) {
m_messagePainter->paint(&m_painter);
}
void PainterGL::showMessage(const QString& message) {
m_messagePainter->showMessage(message);
}

View File

@ -8,8 +8,6 @@
#include "Display.h"
#include "MessagePainter.h"
#include <QGLWidget>
#include <QThread>
#include <QTimer>
@ -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();

View File

@ -7,13 +7,15 @@
#include <QPainter>
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);
}

View File

@ -7,7 +7,6 @@
#define QGBA_DISPLAY_QT
#include "Display.h"
#include "MessagePainter.h"
#include <QImage>
#include <QTimer>
@ -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;
};
}

View File

@ -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<Display::Driver>(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<Display::Driver>(m_configController.getQtOption("videoDriver").toInt()));
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
w->controller()->reloadAudioDriver();

View File

@ -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:

View File

@ -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());
}

View File

@ -106,8 +106,7 @@ void GDBWindow::stopped() {
}
void GDBWindow::failed() {
QMessageBox* failure = new QMessageBox(QMessageBox::Warning, tr("Crash"),
tr("Could not start GDB server"),
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();

View File

@ -8,6 +8,7 @@
#ifdef USE_MAGICK
#include "GBAApp.h"
#include "LogController.h"
#include <QMap>
@ -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);

View File

@ -81,27 +81,12 @@ GameController::GameController(QObject* parent)
};
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<GameControllerRTC*>(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) {
GameController* controller = static_cast<GameController*>(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;
@ -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<GameController*>(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;
}

View File

@ -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;
};
}

View File

@ -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() {

View File

@ -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;
}

View File

@ -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 <QObject>
#include <QStringList>
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

View File

@ -5,38 +5,71 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LogView.h"
#include "LogController.h"
#include <QTextBlock>
#include <QTextCursor>
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) {
void LogView::setLevel(int level, bool set) {
if (level & GBA_LOG_DEBUG) {
m_ui.levelDebug->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_STUB) {
m_ui.levelStub->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_INFO) {
m_ui.levelInfo->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_WARN) {
m_ui.levelWarn->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_ERROR) {
m_ui.levelError->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_FATAL) {
m_ui.levelFatal->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_GAME_ERROR) {
m_ui.levelGameError->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
if (level & GBA_LOG_SWI) {
m_ui.levelSWI->setCheckState(set ? Qt::Checked : Qt::Unchecked);
}
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);
}
if (set) {
setLevel(GBA_LOG_DEBUG);
emit levelsEnabled(level);
} else {
clearLevel(GBA_LOG_DEBUG);
}
}
void LogView::setLevelStub(bool set) {
if (set) {
setLevel(GBA_LOG_STUB);
} else {
clearLevel(GBA_LOG_STUB);
}
}
void LogView::setLevelInfo(bool set) {
if (set) {
setLevel(GBA_LOG_INFO);
} else {
clearLevel(GBA_LOG_INFO);
}
}
void LogView::setLevelWarn(bool set) {
if (set) {
setLevel(GBA_LOG_WARN);
} else {
clearLevel(GBA_LOG_WARN);
}
}
void LogView::setLevelError(bool set) {
if (set) {
setLevel(GBA_LOG_ERROR);
} else {
clearLevel(GBA_LOG_ERROR);
}
}
void LogView::setLevelFatal(bool set) {
if (set) {
setLevel(GBA_LOG_FATAL);
} else {
clearLevel(GBA_LOG_FATAL);
}
}
void LogView::setLevelGameError(bool set) {
if (set) {
setLevel(GBA_LOG_GAME_ERROR);
} else {
clearLevel(GBA_LOG_GAME_ERROR);
}
}
void LogView::setLevelSWI(bool set) {
if (set) {
setLevel(GBA_LOG_SWI);
} else {
clearLevel(GBA_LOG_SWI);
}
}
void LogView::setLevelStatus(bool set) {
if (set) {
setLevel(GBA_LOG_STATUS);
} else {
clearLevel(GBA_LOG_STATUS);
}
}
void LogView::setLevelSIO(bool set) {
if (set) {
setLevel(GBA_LOG_SIO);
} 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);

View File

@ -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();
};

View File

@ -7,6 +7,7 @@
#include "GBAApp.h"
#include "GameController.h"
#include "LogController.h"
#include <QAction>
#include <QApplication>
@ -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;
}
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;
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);
}

View File

@ -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();
}

View File

@ -6,7 +6,9 @@
#ifndef QGBA_MESSAGE_PAINTER
#define QGBA_MESSAGE_PAINTER
#include <QMutex>
#include <QObject>
#include <QPixmap>
#include <QStaticText>
#include <QTimer>
@ -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;
};
}

View File

@ -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);

View File

@ -6,6 +6,7 @@
#include "PaletteView.h"
#include "GBAApp.h"
#include "LogController.h"
#include "VFileDevice.h"
#include <QFileDialog>
@ -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;
}

View File

@ -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);

View File

@ -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<int>(Display::Driver::QT));
if (!displayDriver.isNull() && displayDriver.toInt() == static_cast<int>(Display::Driver::QT)) {
m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1);
}
#ifdef BUILD_GL
m_ui.displayDriver->addItem(tr("OpenGL"), static_cast<int>(Display::Driver::OPENGL));
if (displayDriver.isNull() || displayDriver.toInt() == static_cast<int>(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<Display::Driver>(displayDriver.toInt()));
emit displayDriverChanged();
}
m_controller->write();
emit biosLoaded(m_ui.bios->text());

View File

@ -23,6 +23,7 @@ public:
signals:
void biosLoaded(const QString&);
void audioDriverChanged();
void displayDriverChanged();
private slots:
void selectBios();

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>673</width>
<width>698</width>
<height>366</height>
</rect>
</property>
@ -140,30 +140,12 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Sync:</string>
<string>Display driver:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QCheckBox" name="videoSync">
<property name="text">
<string>Video</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="audioSync">
<property name="text">
<string>Audio</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
@ -230,27 +212,52 @@
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sync:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QCheckBox" name="videoSync">
<property name="text">
<string>Video</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="audioSync">
<property name="text">
<string>Audio</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="lockAspectRatio">
<property name="text">
<string>Lock aspect ratio</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QCheckBox" name="resampleVideo">
<property name="text">
<string>Resample video</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="suspendScreensaver">
<property name="text">
<string>Suspend screensaver</string>
</property>
<property name="checked">
<bool>true</bool>
<item row="4" column="1">
<widget class="QComboBox" name="displayDriver">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
@ -387,14 +394,24 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="9" column="1">
<widget class="QCheckBox" name="suspendScreensaver">
<property name="text">
<string>Suspend screensaver</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Idle loops</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QComboBox" name="idleOptimization">
<item>
<property name="text">

View File

@ -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<void ()> press, std::function<void ()> release, const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> 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<void ()> 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));
}

View File

@ -35,7 +35,8 @@ private:
typedef QPair<std::function<void ()>, std::function<void ()>> 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<void ()> press, std::function<void()> release, const QKeySequence& shortcut, const QString& visibleName, const QString& name);
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
const QKeySequence& shortcut, const QString& visibleName, const QString& name);
void addMenu(QMenu* menu, QMenu* parent = nullptr);
QKeySequence shortcutAt(const QModelIndex& index) const;

View File

@ -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<GamepadAxisEvent::Direction>(direction));
m_controller->updateAxis(m_ui.shortcutTable->selectionModel()->currentIndex(), axis,
static_cast<GamepadAxisEvent::Direction>(direction));
}

View File

@ -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);
}

View File

@ -8,6 +8,7 @@
#ifdef USE_FFMPEG
#include "GBAApp.h"
#include "LogController.h"
#include <QMap>
@ -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;

View File

@ -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() {
@ -552,7 +553,8 @@ void Window::unimplementedBiosCall(int call) {
}
m_hitUnimplementedBiosCall = true;
QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Unimplemented BIOS call"),
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);
@ -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<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32);
QImage currentImage(reinterpret_cast<const uchar*>(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()));

View File

@ -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<QAction*> m_gameActions;
LogController m_log;
LogView* m_logView;
LoadSaveState* m_stateWindow;
WindowBackground* m_screenWidget;

View File

@ -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)

View File

@ -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
}

View File

@ -176,5 +176,4 @@ static void GBASDLDeinit(struct SDLSoftwareRenderer* renderer) {
renderer->deinit(renderer);
SDL_Quit();
}

View File

@ -45,6 +45,7 @@ struct SDLSoftwareRenderer {
SDL_Window* window;
SDL_Texture* sdlTex;
SDL_Renderer* sdlRenderer;
SDL_GLContext* glCtx;
#endif
int viewportWidth;

View File

@ -76,7 +76,6 @@ void GBASDLPauseAudio(struct GBASDLAudio* context) {
UNUSED(context);
SDL_PauseAudio(1);
#endif
}
void GBASDLResumeAudio(struct GBASDLAudio* context) {

View File

@ -111,10 +111,10 @@ void GBASDLSWRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* rend
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:

View File

@ -6,7 +6,9 @@
#ifndef COMMON_H
#define COMMON_H
#ifndef PSP2
#include <ctype.h>
#endif
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
@ -22,12 +24,14 @@
#include "version.h"
#ifdef _MSC_VER
typedef intptr_t off_t;
#include <sys/types.h>
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

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -9,6 +9,8 @@
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/mman.h>
#else
#include <windows.h>
#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);
}

View File

@ -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;
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);
}