mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into feature/input-revamp
This commit is contained in:
commit
079ceead68
67
CHANGES
67
CHANGES
|
@ -4,30 +4,69 @@ Features:
|
|||
- Game Boy Camera support
|
||||
- Qt: Set default Game Boy colors
|
||||
- Game Boy Printer support
|
||||
- Super Game Boy support
|
||||
- Customizable autofire speed
|
||||
- Ability to set default Game Boy model
|
||||
- Map viewer
|
||||
Bugfixes:
|
||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||
- Python: Fix importing .gb or .gba before .core
|
||||
- GBA: Reset active region as needed when loading a ROM
|
||||
- Qt: Fix command line debugger closing second game
|
||||
- GB Serialize: Fix audio state loading
|
||||
- GB Video: Fix dot clock timing being slightly wrong
|
||||
- GB MBC: Pocket Cam memory should be accessible without enabling
|
||||
- GB Memory: Initialize peripheral pointers
|
||||
- GB MBC: Fix SRAM sizes 4 and 5
|
||||
- GB Video: Fix 16-bit screenshots (fixes mgba.io/i/826)
|
||||
- GB Core: Fix palette loading when loading a foreign config
|
||||
- Qt: Fix LOG argument order
|
||||
- GB Memory: Prevent accessing empty SRAM (fixes mgba.io/i/831)
|
||||
- GB, GBA: Fix crashes when attempting to identify null VFiles
|
||||
- GB MBC: Fix RTC initialization (fixes mgba.io/i/825)
|
||||
- GB MBC: Fix RTC loading when file size is off
|
||||
- Qt: Fix GL display when loading a game from CLI (fixes mgba.io/i/843)
|
||||
- ARM: Fix MSR when T bit is set
|
||||
- GB Serialize: Fix game title check
|
||||
- GB: Revamp IRQ handling based on new information
|
||||
Misc:
|
||||
- GBA Timer: Use global cycles for timers
|
||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||
- All: Make FIXED_ROM_BUFFER an option instead of 3DS-only
|
||||
- Qt: Don't rebuild library view if style hasn't changed
|
||||
- Qt: Redo GameController into multiple classes
|
||||
- Test: Restructure test suite into multiple executables
|
||||
- Python: Integrate tests from cinema test suite
|
||||
- Util: Don't build crc32 if the function already exists
|
||||
|
||||
0.6.1: (2017-10-01)
|
||||
Bugfixes:
|
||||
- GB, GBA: Fix crashes when attempting to identify null VFiles
|
||||
- GB, GBA: Fix sync to video with frameskip
|
||||
- GB, GBA Savedata: Fix savestate-related save overwriting (fixes mgba.io/i/834)
|
||||
- GB Audio: Fix NRx2 writes while active (fixes mgba.io/i/866)
|
||||
- GB Core: Fix palette loading when loading a foreign config
|
||||
- GB MBC: Pocket Cam memory should be accessible without enabling
|
||||
- GB MBC: Fix SRAM sizes 4 and 5
|
||||
- GB MBC: Fix RTC initialization (fixes mgba.io/i/825)
|
||||
- GB MBC: Fix RTC loading when file size is off
|
||||
- GB Memory: Initialize peripheral pointers
|
||||
- GB Memory: Prevent accessing empty SRAM (fixes mgba.io/i/831)
|
||||
- GB Memory: Fix HDMA count starting in mode 0 (fixes mgba.io/i/855)
|
||||
- GB Memory: Actually load latch time from savestate
|
||||
- GB Serialize: Fix deserializing video STAT
|
||||
- GB Video: Fix 16-bit screenshots (fixes mgba.io/i/826)
|
||||
- GB Video: Fix potential hang when ending mode 0
|
||||
- GB Video: Fix read mode when enabling LCD
|
||||
- GBA: Reset active region as needed when loading a ROM
|
||||
- GBA: Fix keypad IRQs not firing when extra buttons are pressed
|
||||
- GBA BIOS: Use core's VRAM variable instead of renderer's
|
||||
- GBA Cheats: Fix PARv3 multiline blocks (fixes mgba.io/i/889)
|
||||
- GBA I/O: Fix reading from a few invalid I/O registers (fixes mgba.io/i/876)
|
||||
- GBA Savedata: Fix 512 byte EEPROM saving as 8kB (fixes mgba.io/i/877)
|
||||
- GBA Savedata: Fix size of SRAM saves (fixes mgba.io/i/883)
|
||||
- GBA Video: Fix broken sprite blending hack (fixes mgba.io/i/532)
|
||||
- Python: Fix importing .gb or .gba before .core
|
||||
- Qt: Fix command line debugger closing second game
|
||||
- Qt: Fix LOG argument order
|
||||
- Qt: Fix timezone issues with time overrides
|
||||
- Qt: Fix sprite export pausing game indefinitely (fixes mgba.io/i/841)
|
||||
- SDL: Fix potential race condition when pressing keys (fixes mgba.io/i/872)
|
||||
Misc:
|
||||
- CMake: Fix CPack dependencies for libpng 1.6
|
||||
- GBA: Detect hardware for Pokémon FireRed ROM hacks
|
||||
- GBA Cheats: Improve detection of raw cheats
|
||||
- Qt: Don't rebuild library view if style hasn't changed
|
||||
- Qt: Allow overrides to be saved before a game is loaded
|
||||
- Qt: Hide mouse immediately when loading
|
||||
- SDL: Fix 2.0.5 build on macOS under some circumstances
|
||||
- VFS: Make VFile.truncate work growing files on PSV (fixes mgba.io/i/885)
|
||||
|
||||
0.6.0: (2017-07-16)
|
||||
Features:
|
||||
|
|
|
@ -124,6 +124,7 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES)
|
|||
set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
|
||||
set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE)
|
||||
set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
|
||||
set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE)
|
||||
if (APPLE)
|
||||
set(IS_FRAMEWORK OFF)
|
||||
set(LIBS)
|
||||
|
@ -500,6 +501,14 @@ if(USE_ZLIB)
|
|||
include_directories(AFTER ${ZLIB_INCLUDE_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${ZLIB_LIBRARIES})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},zlib1g")
|
||||
set(HAVE_CRC32 ON)
|
||||
else()
|
||||
# zlib pulls in crc32
|
||||
check_function_exists(crc32 HAVE_CRC32)
|
||||
endif()
|
||||
|
||||
if(HAVE_CRC32)
|
||||
list(APPEND FUNCTION_DEFINES HAVE_CRC32)
|
||||
endif()
|
||||
|
||||
if(WANT_PNG AND USE_ZLIB AND NOT USE_PNG)
|
||||
|
@ -518,7 +527,15 @@ if(USE_PNG)
|
|||
list(APPEND FEATURES PNG)
|
||||
include_directories(AFTER ${PNG_INCLUDE_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libpng12-0")
|
||||
if(PNG_VERSION_STRING)
|
||||
string(REGEX MATCH "^[0-9]+\\.[0-9]+" PNG_VERSION_PARTIAL ${PNG_VERSION_STRING})
|
||||
if(${PNG_VERSION_PARTIAL} STREQUAL "1.6")
|
||||
set(PNG_DEB_VERSION "16-16")
|
||||
else()
|
||||
set(PNG_DEB_VERSION "12-0")
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libpng${PNG_DEB_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WANT_SQLITE3 AND NOT USE_SQLITE3)
|
||||
|
@ -607,10 +624,6 @@ endif()
|
|||
if(USE_ELF)
|
||||
list(APPEND FEATURES ELF)
|
||||
include_directories(AFTER ${LIBELF_INCLUDE_DIRS})
|
||||
find_file(ELF_REPL_H elf_repl.h PATHS ${LIBELF_INCLUDE_DIRS} NO_CMAKE_FIND_ROOT_PATH)
|
||||
if (ELF_REPL_H)
|
||||
add_definitions(-DUSE_ELF_REPL)
|
||||
endif()
|
||||
list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelfg0")
|
||||
endif()
|
||||
|
@ -619,8 +632,7 @@ if(ENABLE_SCRIPTING)
|
|||
list(APPEND ENABLES SCRIPTING)
|
||||
|
||||
if(BUILD_PYTHON)
|
||||
find_program(PYTHON python)
|
||||
include(FindPythonLibs)
|
||||
find_package(PythonLibs ${USE_PYTHON_VERSION})
|
||||
list(APPEND DEPENDENCY_LIB ${PYTHON_LIBRARIES})
|
||||
include_directories(AFTER ${PYTHON_INCLUDE_DIRS})
|
||||
list(APPEND ENABLES PYTHON)
|
||||
|
@ -861,10 +873,16 @@ if(BUILD_SUITE)
|
|||
include_directories(AFTER ${CMOCKA_INCLUDE_DIRS})
|
||||
link_directories(${CMOCKA_LIBRARY_DIRS})
|
||||
|
||||
add_executable(${BINARY_NAME}-suite ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/suite-main.c ${TEST_SRC})
|
||||
target_link_libraries(${BINARY_NAME}-suite ${BINARY_NAME} ${PLATFORM_LIBRARY} cmocka)
|
||||
set_target_properties(${BINARY_NAME}-suite PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
add_test(${BINARY_NAME}-suite ${BINARY_NAME}-suite)
|
||||
foreach(TEST IN LISTS TEST_SRC)
|
||||
string(REPLACE "${CMAKE_SOURCE_DIR}/src/" "" TEST_NAME "${TEST}")
|
||||
string(REPLACE "/" "-" TEST_NAME "${TEST_NAME}")
|
||||
string(REPLACE "-test" "" TEST_NAME "${TEST_NAME}")
|
||||
string(REPLACE ".c" "" TEST_NAME "${TEST_NAME}")
|
||||
add_executable(test-${TEST_NAME} ${TEST})
|
||||
target_link_libraries(test-${TEST_NAME} ${BINARY_NAME} ${PLATFORM_LIBRARY} cmocka)
|
||||
set_target_properties(test-${TEST_NAME} PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
add_test(${TEST_NAME} test-${TEST_NAME})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(BUILD_EXAMPLE)
|
||||
|
|
|
@ -18,6 +18,7 @@ Features
|
|||
- 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.
|
||||
- Game Boy Camera and Game Boy Printer support.
|
||||
- A built-in BIOS implementation, and ability to load external BIOS files.
|
||||
- Turbo/fast-forward support by holding Tab.
|
||||
- Rewind by holding Backquote.
|
||||
|
@ -45,7 +46,6 @@ Features
|
|||
- A comprehensive debug suite.
|
||||
- e-Reader support.
|
||||
- Wireless adapter support.
|
||||
- Game Boy Printer support.
|
||||
|
||||
Supported Platforms
|
||||
-------------------
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
#define ARM_ALGO_H
|
||||
|
||||
#ifdef __arm__
|
||||
void _to16Bit(uint16_t* dest, uint32_t* src, size_t words);
|
||||
|
||||
#if defined(__ARM_NEON)
|
||||
void _neon2x(void* dest, void* src, int width, int height);
|
||||
void _neon4x(void* dest, void* src, int width, int height);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -12,8 +12,13 @@ CXX_GUARD_START
|
|||
|
||||
struct VFile;
|
||||
|
||||
#ifndef HAVE_CRC32
|
||||
uint32_t crc32(uint32_t crc, const void* buf, size_t size);
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
uint32_t doCrc32(const void* buf, size_t size);
|
||||
uint32_t updateCrc32(uint32_t crc, const void* buf, size_t size);
|
||||
uint32_t fileCrc32(struct VFile* file, size_t endOffset);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -14,12 +14,6 @@ CXX_GUARD_START
|
|||
|
||||
#include <libelf.h>
|
||||
|
||||
#if USE_ELF_REPL
|
||||
#include <elf_repl.h>
|
||||
#else
|
||||
#include <elf.h>
|
||||
#endif
|
||||
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct ELF;
|
||||
|
|
|
@ -26,9 +26,11 @@ enum {
|
|||
|
||||
png_structp PNGWriteOpen(struct VFile* source);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||
void PNGWriteClose(png_structp png, png_infop info);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright (c) 2013-2017 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 M_CACHE_SET_H
|
||||
#define M_CACHE_SET_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/map-cache.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
DECLARE_VECTOR(mMapCacheSet, struct mMapCache);
|
||||
DECLARE_VECTOR(mTileCacheSet, struct mTileCache);
|
||||
|
||||
struct mCacheSet {
|
||||
struct mMapCacheSet maps;
|
||||
struct mTileCacheSet tiles;
|
||||
};
|
||||
|
||||
void mCacheSetInit(struct mCacheSet*, size_t nMaps, size_t nTiles);
|
||||
void mCacheSetDeinit(struct mCacheSet*);
|
||||
|
||||
void mCacheSetAssignVRAM(struct mCacheSet*, void* vram);
|
||||
|
||||
void mCacheSetWriteVRAM(struct mCacheSet*, uint32_t address);
|
||||
void mCacheSetWritePalette(struct mCacheSet*, uint32_t entry, color_t color);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -35,6 +35,25 @@ typedef uint32_t color_t;
|
|||
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
|
||||
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
|
||||
|
||||
#ifndef PYCPARSE
|
||||
static inline color_t mColorFrom555(uint16_t value) {
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
color_t color = 0;
|
||||
color |= (value & 0x001F) << 11;
|
||||
color |= (value & 0x03E0) << 1;
|
||||
color |= (value & 0x7C00) >> 10;
|
||||
#else
|
||||
color_t color = value;
|
||||
#endif
|
||||
#else
|
||||
color_t color = M_RGB5_TO_BGR8(value);
|
||||
color |= (color >> 5) & 0x070707;
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct blip_t;
|
||||
|
||||
enum mColorFormat {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* Copyright (c) 2013-2017 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 M_MAP_CACHE_H
|
||||
#define M_MAP_CACHE_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
|
||||
DECL_BITFIELD(mMapCacheConfiguration, uint32_t);
|
||||
DECL_BIT(mMapCacheConfiguration, ShouldStore, 0);
|
||||
|
||||
DECL_BITFIELD(mMapCacheSystemInfo, uint32_t);
|
||||
DECL_BITS(mMapCacheSystemInfo, PaletteBPP, 0, 2);
|
||||
DECL_BITS(mMapCacheSystemInfo, PaletteCount, 2, 4);
|
||||
DECL_BITS(mMapCacheSystemInfo, TilesWide, 8, 4);
|
||||
DECL_BITS(mMapCacheSystemInfo, TilesHigh, 12, 4);
|
||||
DECL_BITS(mMapCacheSystemInfo, MacroTileSize, 16, 7);
|
||||
DECL_BITS(mMapCacheSystemInfo, MapAlign, 23, 2);
|
||||
|
||||
DECL_BITFIELD(mMapCacheEntryFlags, uint16_t);
|
||||
DECL_BITS(mMapCacheEntryFlags, PaletteId, 0, 4);
|
||||
DECL_BIT(mMapCacheEntryFlags, VramClean, 4);
|
||||
DECL_BIT(mMapCacheEntryFlags, HMirror, 5);
|
||||
DECL_BIT(mMapCacheEntryFlags, VMirror, 6);
|
||||
DECL_BITS(mMapCacheEntryFlags, Mirror, 5, 2);
|
||||
|
||||
struct mMapCacheEntry {
|
||||
uint32_t vramVersion;
|
||||
uint16_t tileId;
|
||||
mMapCacheEntryFlags flags;
|
||||
struct mTileCacheEntry tileStatus[16];
|
||||
};
|
||||
|
||||
struct mTileCache;
|
||||
struct mTileCacheEntry;
|
||||
struct mMapCache {
|
||||
color_t* cache;
|
||||
struct mTileCache* tileCache;
|
||||
struct mMapCacheEntry* status;
|
||||
|
||||
uint8_t* vram;
|
||||
|
||||
uint32_t mapStart;
|
||||
uint32_t mapSize;
|
||||
|
||||
uint32_t tileStart;
|
||||
|
||||
mMapCacheConfiguration config;
|
||||
mMapCacheSystemInfo sysConfig;
|
||||
|
||||
void (*mapParser)(struct mMapCache*, struct mMapCacheEntry* entry, void* vram);
|
||||
void* context;
|
||||
};
|
||||
|
||||
void mMapCacheInit(struct mMapCache* cache);
|
||||
void mMapCacheDeinit(struct mMapCache* cache);
|
||||
void mMapCacheConfigure(struct mMapCache* cache, mMapCacheConfiguration config);
|
||||
void mMapCacheConfigureSystem(struct mMapCache* cache, mMapCacheSystemInfo config);
|
||||
void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart);
|
||||
void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address);
|
||||
|
||||
uint32_t mMapCacheTileId(struct mMapCache* cache, unsigned x, unsigned y);
|
||||
|
||||
bool mMapCacheCheckTile(struct mMapCache* cache, const struct mMapCacheEntry* entry, unsigned x, unsigned y);
|
||||
void mMapCacheCleanTile(struct mMapCache* cache, struct mMapCacheEntry* entry, unsigned x, unsigned y);
|
||||
|
||||
void mMapCacheCleanRow(struct mMapCache* cache, unsigned y);
|
||||
const color_t* mMapCacheGetRow(struct mMapCache* cache, unsigned y);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -34,6 +34,8 @@ struct mCoreThread {
|
|||
ThreadCallback cleanCallback;
|
||||
ThreadCallback frameCallback;
|
||||
ThreadCallback sleepCallback;
|
||||
ThreadCallback pauseCallback;
|
||||
ThreadCallback unpauseCallback;
|
||||
void* userData;
|
||||
void (*run)(struct mCoreThread*);
|
||||
|
||||
|
@ -50,13 +52,19 @@ enum mCoreThreadState {
|
|||
THREAD_RUNNING = 0,
|
||||
THREAD_REWINDING,
|
||||
THREAD_MAX_RUNNING = THREAD_REWINDING,
|
||||
|
||||
THREAD_WAITING,
|
||||
THREAD_INTERRUPTED,
|
||||
THREAD_INTERRUPTING,
|
||||
THREAD_PAUSED,
|
||||
THREAD_MAX_WAITING = THREAD_PAUSED,
|
||||
|
||||
THREAD_PAUSING,
|
||||
THREAD_RUN_ON,
|
||||
THREAD_WAITING,
|
||||
THREAD_RESETING,
|
||||
THREAD_MIN_DEFERRED = THREAD_PAUSING,
|
||||
THREAD_MAX_DEFERRED = THREAD_RESETING,
|
||||
|
||||
THREAD_INTERRUPTING,
|
||||
THREAD_EXITING,
|
||||
THREAD_SHUTDOWN,
|
||||
THREAD_CRASHED
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
|
||||
DECL_BITFIELD(mTileCacheConfiguration, uint32_t);
|
||||
DECL_BIT(mTileCacheConfiguration, ShouldStore, 0);
|
||||
|
||||
DECL_BITFIELD(mTileCacheSystemInfo, uint32_t);
|
||||
DECL_BITS(mTileCacheSystemInfo, Palette0BPP, 0, 2);
|
||||
DECL_BITS(mTileCacheSystemInfo, Palette0Count, 2, 4);
|
||||
DECL_BITS(mTileCacheSystemInfo, Palette1BPP, 8, 2);
|
||||
DECL_BITS(mTileCacheSystemInfo, Palette1Count, 10, 4);
|
||||
DECL_BITS(mTileCacheSystemInfo, PaletteBPP, 0, 2);
|
||||
DECL_BITS(mTileCacheSystemInfo, PaletteCount, 2, 4);
|
||||
DECL_BITS(mTileCacheSystemInfo, MaxTiles, 16, 13);
|
||||
|
||||
struct mTileCacheEntry {
|
||||
|
@ -25,24 +25,22 @@ struct mTileCacheEntry {
|
|||
uint32_t vramVersion;
|
||||
uint8_t vramClean;
|
||||
uint8_t paletteId;
|
||||
uint8_t activePalette;
|
||||
uint8_t padding;
|
||||
uint16_t padding;
|
||||
};
|
||||
|
||||
struct mTileCache {
|
||||
uint16_t* cache;
|
||||
color_t* cache;
|
||||
struct mTileCacheEntry* status;
|
||||
uint32_t* globalPaletteVersion[2];
|
||||
uint32_t* globalPaletteVersion;
|
||||
|
||||
int activePalette;
|
||||
unsigned entries;
|
||||
unsigned count;
|
||||
uint32_t tileBase;
|
||||
uint32_t paletteBase;
|
||||
unsigned entriesPerTile;
|
||||
unsigned bpp;
|
||||
|
||||
uint16_t* vram;
|
||||
uint16_t* palette;
|
||||
uint16_t temporaryTile[64];
|
||||
color_t* palette;
|
||||
color_t temporaryTile[64];
|
||||
|
||||
mTileCacheConfiguration config;
|
||||
mTileCacheSystemInfo sysConfig;
|
||||
|
@ -51,15 +49,14 @@ struct mTileCache {
|
|||
void mTileCacheInit(struct mTileCache* cache);
|
||||
void mTileCacheDeinit(struct mTileCache* cache);
|
||||
void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config);
|
||||
void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config);
|
||||
void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config, uint32_t tileBase, uint32_t paletteBase);
|
||||
void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address);
|
||||
void mTileCacheWritePalette(struct mTileCache* cache, uint32_t address);
|
||||
void mTileCacheSetPalette(struct mTileCache* cache, int palette);
|
||||
void mTileCacheWritePalette(struct mTileCache* cache, uint32_t entry, color_t color);
|
||||
|
||||
const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId);
|
||||
const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId);
|
||||
const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId);
|
||||
const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId);
|
||||
const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId);
|
||||
const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId);
|
||||
const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId);
|
||||
const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ struct mTimingEvent {
|
|||
|
||||
struct mTiming {
|
||||
struct mTimingEvent* root;
|
||||
struct mTimingEvent* reroot;
|
||||
|
||||
uint32_t masterCycles;
|
||||
int32_t* relativeCycles;
|
||||
|
|
|
@ -12,10 +12,12 @@ CXX_GUARD_START
|
|||
|
||||
enum GBModel {
|
||||
GB_MODEL_AUTODETECT = 0xFF,
|
||||
GB_MODEL_DMG = 0x00,
|
||||
GB_MODEL_SGB = 0x40,
|
||||
GB_MODEL_CGB = 0x80,
|
||||
GB_MODEL_AGB = 0xC0
|
||||
GB_MODEL_DMG = 0x00,
|
||||
GB_MODEL_SGB = 0x20,
|
||||
GB_MODEL_MGB = 0x40,
|
||||
GB_MODEL_SGB2 = 0x60,
|
||||
GB_MODEL_CGB = 0x80,
|
||||
GB_MODEL_AGB = 0xC0
|
||||
};
|
||||
|
||||
enum GBMemoryBankControllerType {
|
||||
|
@ -45,6 +47,9 @@ struct GBSIODriver {
|
|||
uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value);
|
||||
};
|
||||
|
||||
enum GBModel GBNameToModel(const char*);
|
||||
const char* GBModelToName(enum GBModel);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -142,6 +142,7 @@ struct GBAudioNoiseChannel {
|
|||
|
||||
enum GBAudioStyle {
|
||||
GB_AUDIO_DMG,
|
||||
GB_AUDIO_MGB = GB_AUDIO_DMG, // TODO
|
||||
GB_AUDIO_CGB,
|
||||
GB_AUDIO_AGB, // GB in GBA
|
||||
GB_AUDIO_GBA, // GBA PSG
|
||||
|
|
|
@ -44,6 +44,34 @@ enum GBIRQVector {
|
|||
GB_VECTOR_KEYPAD = 0x60,
|
||||
};
|
||||
|
||||
enum GBSGBCommand {
|
||||
SGB_PAL01 = 0,
|
||||
SGB_PAL23,
|
||||
SGB_PAL03,
|
||||
SGB_PAL12,
|
||||
SGB_ATTR_BLK,
|
||||
SGB_ATTR_LIN,
|
||||
SGB_ATTR_DIV,
|
||||
SGB_ATTR_CHR,
|
||||
SGB_SOUND,
|
||||
SGB_SOU_TRN,
|
||||
SGB_PAL_SET,
|
||||
SGB_PAL_TRN,
|
||||
SGB_ATRC_EN,
|
||||
SGB_TEST_EN,
|
||||
SGB_PICON_EN,
|
||||
SGB_DATA_SND,
|
||||
SGB_DATA_TRN,
|
||||
SGB_MLT_REG,
|
||||
SGB_JUMP,
|
||||
SGB_CHR_TRN,
|
||||
SGB_PCT_TRN,
|
||||
SGB_ATTR_TRN,
|
||||
SGB_ATTR_SET,
|
||||
SGB_MASK_EN,
|
||||
SGB_OBJ_TRN
|
||||
};
|
||||
|
||||
struct LR35902Core;
|
||||
struct mCoreSync;
|
||||
struct mAVStream;
|
||||
|
@ -76,6 +104,10 @@ struct GB {
|
|||
int32_t sramDirtAge;
|
||||
bool sramMaskWriteback;
|
||||
|
||||
int sgbBit;
|
||||
int currentSgbBits;
|
||||
uint8_t sgbPacket[16];
|
||||
|
||||
struct mCoreCallbacksList coreCallbacks;
|
||||
struct mAVStream* stream;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ struct GBMemory;
|
|||
void GBMBCInit(struct GB* gb);
|
||||
void GBMBCSwitchBank(struct GB* gb, int bank);
|
||||
void GBMBCSwitchBank0(struct GB* gb, int bank);
|
||||
void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank);
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank);
|
||||
|
||||
enum GBCam {
|
||||
|
|
|
@ -22,6 +22,8 @@ struct GB;
|
|||
enum {
|
||||
GB_BASE_CART_BANK0 = 0x0000,
|
||||
GB_BASE_CART_BANK1 = 0x4000,
|
||||
GB_BASE_CART_HALFBANK1 = 0x4000,
|
||||
GB_BASE_CART_HALFBANK2 = 0x6000,
|
||||
GB_BASE_VRAM = 0x8000,
|
||||
GB_BASE_EXTERNAL_RAM = 0xA000,
|
||||
GB_BASE_WORKING_RAM_BANK0 = 0xC000,
|
||||
|
@ -46,6 +48,7 @@ enum {
|
|||
|
||||
enum {
|
||||
GB_SIZE_CART_BANK0 = 0x4000,
|
||||
GB_SIZE_CART_HALFBANK = 0x2000,
|
||||
GB_SIZE_CART_MAX = 0x800000,
|
||||
GB_SIZE_VRAM = 0x4000,
|
||||
GB_SIZE_VRAM_BANK0 = 0x2000,
|
||||
|
@ -104,6 +107,11 @@ struct GBMBC1State {
|
|||
int multicartStride;
|
||||
};
|
||||
|
||||
struct GBMBC6State {
|
||||
int currentBank1;
|
||||
uint8_t* romBank1;
|
||||
};
|
||||
|
||||
struct GBMBC7State {
|
||||
enum GBMBC7MachineState state;
|
||||
uint16_t sr;
|
||||
|
@ -127,6 +135,7 @@ struct GBTAMA5State {
|
|||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC6State mbc6;
|
||||
struct GBMBC7State mbc7;
|
||||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
|
|
|
@ -17,7 +17,7 @@ struct GBCartridgeOverride {
|
|||
enum GBModel model;
|
||||
enum GBMemoryBankControllerType mbc;
|
||||
|
||||
uint32_t gbColors[4];
|
||||
uint32_t gbColors[12];
|
||||
};
|
||||
|
||||
struct Configuration;
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
struct GBVideo;
|
||||
struct mTileCache;
|
||||
struct mCacheSet;
|
||||
|
||||
void GBVideoTileCacheInit(struct mTileCache* cache);
|
||||
void GBVideoTileCacheAssociate(struct mTileCache* cache, struct GBVideo* video);
|
||||
void GBVideoCacheInit(struct mCacheSet* cache);
|
||||
void GBVideoCacheAssociate(struct mCacheSet* cache, struct GBVideo* video);
|
||||
|
||||
void GBVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint16_t address, uint8_t value);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
@ -23,6 +23,7 @@ struct GBVideoSoftwareRenderer {
|
|||
uint8_t row[GB_VIDEO_HORIZONTAL_PIXELS + 8];
|
||||
|
||||
color_t palette[128];
|
||||
uint8_t lookup[64];
|
||||
|
||||
uint32_t* temporaryBuffer;
|
||||
|
||||
|
@ -34,6 +35,14 @@ struct GBVideoSoftwareRenderer {
|
|||
|
||||
GBRegisterLCDC lcdc;
|
||||
enum GBModel model;
|
||||
|
||||
int sgbTransfer;
|
||||
uint8_t sgbPacket[16];
|
||||
uint8_t sgbCommandHeader;
|
||||
int sgbPacketId;
|
||||
int sgbDataSets;
|
||||
uint8_t sgbPartialDataSet[15];
|
||||
bool sgbBorders;
|
||||
};
|
||||
|
||||
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
|
||||
|
|
|
@ -19,7 +19,7 @@ extern const uint32_t GB_SAVESTATE_VERSION;
|
|||
mLOG_DECLARE_CATEGORY(GB_STATE);
|
||||
|
||||
/* Savestate format:
|
||||
* 0x00000 - 0x00003: Version Magic (0x01000001)
|
||||
* 0x00000 - 0x00003: Version Magic (0x01000002)
|
||||
* 0x00004 - 0x00007: ROM CRC32
|
||||
* 0x00008: Game Boy model
|
||||
* 0x00009 - 0x0000B: Reserved (leave zero)
|
||||
|
@ -42,7 +42,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* | 0x00036 - 0x00037: Index address
|
||||
* | 0x00038: Bus value
|
||||
* | 0x00039: Execution state
|
||||
* | 0x0003A - 0x0003B: IRQ vector
|
||||
* | 0x0003A - 0x0003B: Reserved
|
||||
* | 0x0003C - 0x0003F: EI pending cycles
|
||||
* | 0x00040 - 0x00043: Reserved (DI pending cycles)
|
||||
* | 0x00044 - 0x00047: Flags
|
||||
|
@ -161,7 +161,22 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* 0x003FF: Interrupts enabled
|
||||
* 0x00400 - 0x043FF: VRAM
|
||||
* 0x04400 - 0x0C3FF: WRAM
|
||||
* Total size: 0xC400 (50,176) bytes
|
||||
* 0x0C400 - 0x0C77F: Reserved
|
||||
* 0x0C780 - 0x117FF: Super Game Boy
|
||||
* | 0x0C780 - 0x0C7D9: Current attributes
|
||||
* | 0x0C7DA: Current command
|
||||
* | 0x0C7DB: Current bit count
|
||||
* | 0x0C7DC - 0x0C7DF: Flags
|
||||
* | bits 0 - 1: Current P1 bits
|
||||
* | bits 2 - 3: Current render mode
|
||||
* | bits 4 - 31: Reserved (leave 0)
|
||||
* | 0x0C7E0 - 0x0C7EF: Current packet
|
||||
* | 0x0C7F0 - 0x0C7FF: Reserved
|
||||
* | 0x0C800 - 0x0E7FF: Character VRAM
|
||||
* | 0x0E800 - 0x0F7FF: Tile map VRAM
|
||||
* | 0x0F800 - 0x107FF: Palette VRAM
|
||||
* | 0x10800 - 0x117FF: Attribute file
|
||||
* Total size: 0x11800 (71,680) bytes
|
||||
*/
|
||||
|
||||
DECL_BITFIELD(GBSerializedAudioFlags, uint32_t);
|
||||
|
@ -238,6 +253,10 @@ DECL_BIT(GBSerializedMemoryFlags, Ime, 3);
|
|||
DECL_BIT(GBSerializedMemoryFlags, IsHdma, 4);
|
||||
DECL_BITS(GBSerializedMemoryFlags, ActiveRtcReg, 5, 3);
|
||||
|
||||
DECL_BITFIELD(GBSerializedSGBFlags, uint32_t);
|
||||
DECL_BITS(GBSerializedSGBFlags, P1Bits, 0, 2);
|
||||
DECL_BITS(GBSerializedSGBFlags, RenderMode, 2, 2);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct GBSerializedState {
|
||||
uint32_t versionMagic;
|
||||
|
@ -268,7 +287,7 @@ struct GBSerializedState {
|
|||
uint8_t bus;
|
||||
uint8_t executionState;
|
||||
|
||||
uint16_t irqVector;
|
||||
uint16_t reserved;
|
||||
|
||||
uint32_t eiPending;
|
||||
int32_t reservedDiPending;
|
||||
|
@ -366,6 +385,21 @@ struct GBSerializedState {
|
|||
|
||||
uint8_t vram[GB_SIZE_VRAM];
|
||||
uint8_t wram[GB_SIZE_WORKING_RAM];
|
||||
|
||||
uint32_t reserved2[0xE0];
|
||||
|
||||
struct {
|
||||
uint8_t attributes[90];
|
||||
uint8_t command;
|
||||
uint8_t bits;
|
||||
GBSerializedSGBFlags flags;
|
||||
uint8_t packet[16];
|
||||
uint32_t reserved[4];
|
||||
uint8_t charRam[SGB_SIZE_CHAR_RAM];
|
||||
uint8_t mapRam[SGB_SIZE_MAP_RAM];
|
||||
uint8_t palRam[SGB_SIZE_PAL_RAM];
|
||||
uint8_t atfRam[SGB_SIZE_ATF_RAM];
|
||||
} sgb;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
|
|
@ -30,7 +30,12 @@ enum {
|
|||
GB_VIDEO_TOTAL_LENGTH = 70224,
|
||||
|
||||
GB_BASE_MAP = 0x1800,
|
||||
GB_SIZE_MAP = 0x0400
|
||||
GB_SIZE_MAP = 0x0400,
|
||||
|
||||
SGB_SIZE_CHAR_RAM = 0x2000,
|
||||
SGB_SIZE_MAP_RAM = 0x1000,
|
||||
SGB_SIZE_PAL_RAM = 0x1000,
|
||||
SGB_SIZE_ATF_RAM = 0x1000
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBObjAttributes, uint8_t);
|
||||
|
@ -41,6 +46,13 @@ DECL_BIT(GBObjAttributes, XFlip, 5);
|
|||
DECL_BIT(GBObjAttributes, YFlip, 6);
|
||||
DECL_BIT(GBObjAttributes, Priority, 7);
|
||||
|
||||
DECL_BITFIELD(SGBBgAttributes, uint16_t);
|
||||
DECL_BITS(SGBBgAttributes, Tile, 0, 10);
|
||||
DECL_BITS(SGBBgAttributes, Palette, 10, 3);
|
||||
DECL_BIT(SGBBgAttributes, Priority, 13);
|
||||
DECL_BIT(SGBBgAttributes, XFlip, 14);
|
||||
DECL_BIT(SGBBgAttributes, YFlip, 15);
|
||||
|
||||
struct GBObj {
|
||||
uint8_t y;
|
||||
uint8_t x;
|
||||
|
@ -53,12 +65,13 @@ union GBOAM {
|
|||
uint8_t raw[160];
|
||||
};
|
||||
|
||||
struct mTileCache;
|
||||
struct mCacheSet;
|
||||
struct GBVideoRenderer {
|
||||
void (*init)(struct GBVideoRenderer* renderer, enum GBModel model);
|
||||
void (*init)(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
|
||||
void (*deinit)(struct GBVideoRenderer* renderer);
|
||||
|
||||
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
void (*writeSGBPacket)(struct GBVideoRenderer* renderer, uint8_t* data);
|
||||
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
|
@ -71,7 +84,14 @@ struct GBVideoRenderer {
|
|||
|
||||
uint8_t* vram;
|
||||
union GBOAM* oam;
|
||||
struct mTileCache* cache;
|
||||
struct mCacheSet* cache;
|
||||
|
||||
uint8_t* sgbCharRam;
|
||||
uint8_t* sgbMapRam;
|
||||
uint8_t* sgbPalRam;
|
||||
int sgbRenderMode;
|
||||
uint8_t* sgbAttributes;
|
||||
uint8_t* sgbAttributeFiles;
|
||||
|
||||
bool disableBG;
|
||||
bool disableOBJ;
|
||||
|
@ -123,10 +143,13 @@ struct GBVideo {
|
|||
bool bcpIncrement;
|
||||
int ocpIndex;
|
||||
bool ocpIncrement;
|
||||
uint8_t sgbCommandHeader;
|
||||
|
||||
uint16_t dmgPalette[4];
|
||||
uint16_t dmgPalette[12];
|
||||
uint16_t palette[64];
|
||||
|
||||
bool sgbBorders;
|
||||
|
||||
int32_t frameCounter;
|
||||
int frameskip;
|
||||
int frameskipCounter;
|
||||
|
@ -144,8 +167,11 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value);
|
|||
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);
|
||||
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
|
||||
|
||||
void GBVideoDisableCGB(struct GBVideo* video);
|
||||
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color);
|
||||
|
||||
void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data);
|
||||
|
||||
struct GBSerializedState;
|
||||
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
|
||||
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state);
|
||||
|
|
|
@ -64,10 +64,11 @@ enum {
|
|||
SIZE_CART0 = 0x02000000,
|
||||
SIZE_CART1 = 0x02000000,
|
||||
SIZE_CART2 = 0x02000000,
|
||||
SIZE_CART_SRAM = 0x00010000,
|
||||
SIZE_CART_SRAM = 0x00008000,
|
||||
SIZE_CART_FLASH512 = 0x00010000,
|
||||
SIZE_CART_FLASH1M = 0x00020000,
|
||||
SIZE_CART_EEPROM = 0x00002000
|
||||
SIZE_CART_EEPROM = 0x00002000,
|
||||
SIZE_CART_EEPROM512 = 0x00000200
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -28,7 +28,7 @@ void GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* o
|
|||
|
||||
struct GBA;
|
||||
void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*);
|
||||
void GBAOverrideApplyDefaults(struct GBA*);
|
||||
void GBAOverrideApplyDefaults(struct GBA*, const struct Configuration*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
* 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 GBA_TILE_CACHE_H
|
||||
#define GBA_TILE_CACHE_H
|
||||
#ifndef GBA_CACHE_SET_H
|
||||
#define GBA_CACHE_SET_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct GBAVideo;
|
||||
struct mTileCache;
|
||||
struct mCacheSet;
|
||||
|
||||
void GBAVideoTileCacheInit(struct mTileCache* cache);
|
||||
void GBAVideoTileCacheAssociate(struct mTileCache* cache, struct GBAVideo* video);
|
||||
void GBAVideoCacheInit(struct mCacheSet* cache);
|
||||
void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video);
|
||||
void GBAVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint32_t address, uint16_t value);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
struct GBAVideoSoftwareSprite {
|
||||
|
@ -157,6 +158,14 @@ struct GBAVideoSoftwareRenderer {
|
|||
int oamMax;
|
||||
struct GBAVideoSoftwareSprite sprites[128];
|
||||
|
||||
uint32_t scanlineDirty[5];
|
||||
uint16_t nextIo[REG_SOUND1CNT_LO];
|
||||
struct ScanlineCache {
|
||||
uint16_t io[REG_SOUND1CNT_LO];
|
||||
int32_t scale[2][2];
|
||||
} cache[VIDEO_VERTICAL_PIXELS];
|
||||
int nextY;
|
||||
|
||||
int start;
|
||||
int end;
|
||||
};
|
||||
|
|
|
@ -164,7 +164,7 @@ struct GBAVideoRenderer {
|
|||
uint16_t* palette;
|
||||
uint16_t* vram;
|
||||
union GBAOAM* oam;
|
||||
struct mTileCache* cache;
|
||||
struct mCacheSet* cache;
|
||||
|
||||
bool disableBG[4];
|
||||
bool disableOBJ;
|
||||
|
|
|
@ -66,6 +66,7 @@ struct LR35902InterruptHandler {
|
|||
void (*reset)(struct LR35902Core* cpu);
|
||||
void (*processEvents)(struct LR35902Core* cpu);
|
||||
void (*setInterrupts)(struct LR35902Core* cpu, bool enable);
|
||||
uint16_t (*irqVector)(struct LR35902Core* cpu);
|
||||
void (*halt)(struct LR35902Core* cpu);
|
||||
void (*stop)(struct LR35902Core* cpu);
|
||||
|
||||
|
@ -118,7 +119,6 @@ struct LR35902Core {
|
|||
LR35902Instruction instruction;
|
||||
|
||||
bool irqPending;
|
||||
uint16_t irqVector;
|
||||
|
||||
struct LR35902Memory memory;
|
||||
struct LR35902InterruptHandler irqh;
|
||||
|
@ -136,7 +136,7 @@ void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot);
|
|||
void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot);
|
||||
|
||||
void LR35902Reset(struct LR35902Core* cpu);
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector);
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu);
|
||||
|
||||
void LR35902Tick(struct LR35902Core* cpu);
|
||||
void LR35902Run(struct LR35902Core* cpu);
|
||||
|
|
|
@ -2,8 +2,8 @@ Jaime J. Denizard
|
|||
Fog
|
||||
Reilly Grant
|
||||
Philip Horton
|
||||
Jordan Jorgensen
|
||||
mars
|
||||
pr1ntf
|
||||
Rohit Nirmal
|
||||
Rhys Powell
|
||||
rootfather
|
||||
|
|
|
@ -663,8 +663,9 @@ DEFINE_INSTRUCTION_ARM(MSR,
|
|||
}
|
||||
_ARMReadCPSR(cpu);
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
LOAD_16(cpu->prefetch[0], (cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
cpu->prefetch[0] = 0x46C0; // nop
|
||||
cpu->prefetch[1] &= 0xFFFF;
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
|
||||
} else {
|
||||
LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
|
@ -704,8 +705,9 @@ DEFINE_INSTRUCTION_ARM(MSRI,
|
|||
}
|
||||
_ARMReadCPSR(cpu);
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
LOAD_16(cpu->prefetch[0], (cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
cpu->prefetch[0] = 0x46C0; // nop
|
||||
cpu->prefetch[1] &= 0xFFFF;
|
||||
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
|
||||
} else {
|
||||
LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* Copyright (c) 2013-2017 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 <mgba/core/cache-set.h>
|
||||
|
||||
DEFINE_VECTOR(mMapCacheSet, struct mMapCache);
|
||||
DEFINE_VECTOR(mTileCacheSet, struct mTileCache);
|
||||
|
||||
void mCacheSetInit(struct mCacheSet* cache, size_t nMaps, size_t nTiles) {
|
||||
mMapCacheSetInit(&cache->maps, nMaps);
|
||||
mMapCacheSetResize(&cache->maps, nMaps);
|
||||
mTileCacheSetInit(&cache->tiles, nTiles);
|
||||
mTileCacheSetResize(&cache->tiles, nTiles);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < nMaps; ++i) {
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, i));
|
||||
}
|
||||
for (i = 0; i < nTiles; ++i) {
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, i));
|
||||
}
|
||||
}
|
||||
|
||||
void mCacheSetDeinit(struct mCacheSet* cache) {
|
||||
size_t i;
|
||||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
|
||||
}
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
|
||||
}
|
||||
}
|
||||
|
||||
void mCacheSetAssignVRAM(struct mCacheSet* cache, void* vram) {
|
||||
size_t i;
|
||||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||
mMapCacheSetGetPointer(&cache->maps, i)->vram = vram;
|
||||
}
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
struct mTileCache* tileCache = mTileCacheSetGetPointer(&cache->tiles, i);
|
||||
tileCache->vram = (void*) ((uintptr_t) vram + tileCache->tileBase);
|
||||
}
|
||||
}
|
||||
|
||||
void mCacheSetWriteVRAM(struct mCacheSet* cache, uint32_t address) {
|
||||
size_t i;
|
||||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||
mMapCacheWriteVRAM(mMapCacheSetGetPointer(&cache->maps, i), address);
|
||||
}
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
mTileCacheWriteVRAM(mTileCacheSetGetPointer(&cache->tiles, i), address);
|
||||
}
|
||||
}
|
||||
|
||||
void mCacheSetWritePalette(struct mCacheSet* cache, uint32_t entry, color_t color) {
|
||||
size_t i;
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
mTileCacheWritePalette(mTileCacheSetGetPointer(&cache->tiles, i), entry, color);
|
||||
}
|
||||
}
|
|
@ -256,35 +256,23 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
|||
if (!cheats->enabled) {
|
||||
return;
|
||||
}
|
||||
bool condition = true;
|
||||
int conditionRemaining = 0;
|
||||
int negativeConditionRemaining = 0;
|
||||
cheats->refresh(cheats, device);
|
||||
|
||||
size_t elseLoc = 0;
|
||||
size_t endLoc = 0;
|
||||
size_t nCodes = mCheatListSize(&cheats->list);
|
||||
size_t i;
|
||||
for (i = 0; i < nCodes; ++i) {
|
||||
if (conditionRemaining > 0) {
|
||||
--conditionRemaining;
|
||||
if (!condition) {
|
||||
continue;
|
||||
}
|
||||
} else if (negativeConditionRemaining > 0) {
|
||||
conditionRemaining = negativeConditionRemaining - 1;
|
||||
negativeConditionRemaining = 0;
|
||||
condition = !condition;
|
||||
if (!condition) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
condition = true;
|
||||
}
|
||||
struct mCheat* cheat = mCheatListGetPointer(&cheats->list, i);
|
||||
int32_t value = 0;
|
||||
int32_t operand = cheat->operand;
|
||||
uint32_t operationsRemaining = cheat->repeat;
|
||||
uint32_t address = cheat->address;
|
||||
bool performAssignment = false;
|
||||
bool condition = true;
|
||||
int conditionRemaining = 0;
|
||||
int negativeConditionRemaining = 0;
|
||||
|
||||
for (; operationsRemaining; --operationsRemaining) {
|
||||
switch (cheat->type) {
|
||||
case CHEAT_ASSIGN:
|
||||
|
@ -312,46 +300,55 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
|||
condition = _readMem(device->p, address, cheat->width) == operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_NE:
|
||||
condition = _readMem(device->p, address, cheat->width) != operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_LT:
|
||||
condition = _readMem(device->p, address, cheat->width) < operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_GT:
|
||||
condition = _readMem(device->p, address, cheat->width) > operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_ULT:
|
||||
condition = (uint32_t) _readMem(device->p, address, cheat->width) < (uint32_t) operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_UGT:
|
||||
condition = (uint32_t) _readMem(device->p, address, cheat->width) > (uint32_t) operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_AND:
|
||||
condition = _readMem(device->p, address, cheat->width) & operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_LAND:
|
||||
condition = _readMem(device->p, address, cheat->width) && operand;
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
case CHEAT_IF_NAND:
|
||||
condition = !(_readMem(device->p, address, cheat->width) & operand);
|
||||
conditionRemaining = cheat->repeat;
|
||||
negativeConditionRemaining = cheat->negativeRepeat;
|
||||
operationsRemaining = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -362,6 +359,18 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
|||
address += cheat->addressOffset;
|
||||
operand += cheat->operandOffset;
|
||||
}
|
||||
|
||||
|
||||
if (elseLoc && i == elseLoc) {
|
||||
i = endLoc;
|
||||
endLoc = 0;
|
||||
}
|
||||
if (conditionRemaining > 0 && !condition) {
|
||||
i += conditionRemaining;
|
||||
} else if (negativeConditionRemaining > 0) {
|
||||
elseLoc = i + conditionRemaining;
|
||||
endLoc = elseLoc + negativeConditionRemaining;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/* Copyright (c) 2013-2017 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 <mgba/core/map-cache.h>
|
||||
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
void mMapCacheInit(struct mMapCache* cache) {
|
||||
// TODO: Reconfigurable cache for space savings
|
||||
cache->cache = NULL;
|
||||
cache->config = mMapCacheConfigurationFillShouldStore(0);
|
||||
cache->status = NULL;
|
||||
}
|
||||
|
||||
static void _freeCache(struct mMapCache* cache) {
|
||||
size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
|
||||
mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles);
|
||||
mappedMemoryFree(cache->status, tiles * sizeof(*cache->status));
|
||||
cache->cache = NULL;
|
||||
cache->status = NULL;
|
||||
}
|
||||
|
||||
static void _redoCacheSize(struct mMapCache* cache) {
|
||||
if (!mMapCacheConfigurationIsShouldStore(cache->config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
|
||||
cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles);
|
||||
cache->status = anonymousMemoryMap(tiles * sizeof(*cache->status));
|
||||
}
|
||||
|
||||
void mMapCacheConfigure(struct mMapCache* cache, mMapCacheConfiguration config) {
|
||||
if (config == cache->config) {
|
||||
return;
|
||||
}
|
||||
_freeCache(cache);
|
||||
cache->config = config;
|
||||
_redoCacheSize(cache);
|
||||
}
|
||||
|
||||
void mMapCacheConfigureSystem(struct mMapCache* cache, mMapCacheSystemInfo config) {
|
||||
if (config == cache->sysConfig) {
|
||||
return;
|
||||
}
|
||||
_freeCache(cache);
|
||||
cache->sysConfig = config;
|
||||
_redoCacheSize(cache);
|
||||
|
||||
size_t mapSize = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
|
||||
cache->mapSize = mapSize << mMapCacheSystemInfoGetMapAlign(cache->sysConfig);
|
||||
}
|
||||
|
||||
void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart) {
|
||||
size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
|
||||
memset(cache->status, 0, tiles * sizeof(*cache->status));
|
||||
cache->mapStart = mapStart;
|
||||
}
|
||||
|
||||
void mMapCacheDeinit(struct mMapCache* cache) {
|
||||
_freeCache(cache);
|
||||
}
|
||||
|
||||
void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address) {
|
||||
if (address >= cache->mapStart && address < cache->mapStart + cache->mapSize) {
|
||||
address -= cache->mapStart;
|
||||
struct mMapCacheEntry* status = &cache->status[address >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig)];
|
||||
++status->vramVersion;
|
||||
status->flags = mMapCacheEntryFlagsClearVramClean(status->flags);
|
||||
status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _cleanTile(struct mMapCache* cache, const color_t* tile, color_t* mapOut, const struct mMapCacheEntry* status) {
|
||||
size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
int x, y;
|
||||
switch (mMapCacheEntryFlagsGetMirror(status->flags)) {
|
||||
case 0:
|
||||
memcpy(mapOut, tile, sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride], &tile[0x08], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 2], &tile[0x10], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 3], &tile[0x18], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 4], &tile[0x20], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 5], &tile[0x28], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 6], &tile[0x30], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 7], &tile[0x38], sizeof(color_t) * 8);
|
||||
break;
|
||||
case 1:
|
||||
for (y = 0; y < 8; ++y) {
|
||||
for (x = 0; x < 8; ++x) {
|
||||
mapOut[y * stride + (7 - x)] = tile[y * 8 + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
memcpy(&mapOut[stride * 7], tile, sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 6], &tile[0x08], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 5], &tile[0x10], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 4], &tile[0x18], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 3], &tile[0x20], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride * 2], &tile[0x28], sizeof(color_t) * 8);
|
||||
memcpy(&mapOut[stride], &tile[0x30], sizeof(color_t) * 8);
|
||||
memcpy(mapOut, &tile[0x38], sizeof(color_t) * 8);
|
||||
break;
|
||||
case 3:
|
||||
for (y = 0; y < 8; ++y) {
|
||||
for (x = 0; x < 8; ++x) {
|
||||
mapOut[(7 - y) * stride + (7 - x)] = tile[y * 8 + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mMapCacheTileId(struct mMapCache* cache, unsigned x, unsigned y) {
|
||||
int tilesWide = mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
int tilesHigh = mMapCacheSystemInfoGetTilesHigh(cache->sysConfig);
|
||||
int stride = 1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
|
||||
x &= (1 << tilesWide) - 1;
|
||||
y &= (1 << tilesHigh) - 1;
|
||||
unsigned xMajor = x & ~(stride - 1);
|
||||
unsigned yMajor = y >> mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
|
||||
x &= stride - 1;
|
||||
y &= stride - 1;
|
||||
yMajor <<= tilesWide;
|
||||
y += xMajor + yMajor;
|
||||
return stride * y + x;
|
||||
}
|
||||
|
||||
void mMapCacheCleanTile(struct mMapCache* cache, struct mMapCacheEntry* entry, unsigned x, unsigned y) {
|
||||
size_t location = mMapCacheTileId(cache, x, y);
|
||||
struct mMapCacheEntry* status = &cache->status[location];
|
||||
const color_t* tile = NULL;
|
||||
if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
|
||||
status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
|
||||
cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
|
||||
}
|
||||
unsigned tileId = status->tileId + cache->tileStart;
|
||||
if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
|
||||
tileId = 0;
|
||||
}
|
||||
tile = mTileCacheGetTileIfDirty(cache->tileCache, status->tileStatus, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
|
||||
if (!tile) {
|
||||
if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
|
||||
return;
|
||||
}
|
||||
tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
|
||||
}
|
||||
|
||||
size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
color_t* mapOut = &cache->cache[(y * stride + x) * 8];
|
||||
_cleanTile(cache, tile, mapOut, status);
|
||||
entry[location] = *status;
|
||||
}
|
||||
|
||||
bool mMapCacheCheckTile(struct mMapCache* cache, const struct mMapCacheEntry* entry, unsigned x, unsigned y) {
|
||||
size_t location = mMapCacheTileId(cache, x, y);
|
||||
struct mMapCacheEntry* status = &cache->status[location];
|
||||
int paletteId = mMapCacheEntryFlagsGetPaletteId(status->flags);
|
||||
const color_t* tile = NULL;
|
||||
if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
|
||||
unsigned tileId = status->tileId + cache->tileStart;
|
||||
if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
|
||||
tileId = 0;
|
||||
}
|
||||
tile = mTileCacheGetTileIfDirty(cache->tileCache, &status->tileStatus[paletteId], tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
|
||||
return !tile;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mMapCacheCleanRow(struct mMapCache* cache, unsigned y) {
|
||||
// TODO: Cache
|
||||
int tilesWide = 1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
int macroTile = (1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig)) - 1;
|
||||
size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
int location = 0;
|
||||
int x;
|
||||
for (x = 0; x < tilesWide; ++x) {
|
||||
if (!(x & macroTile)) {
|
||||
location = mMapCacheTileId(cache, x, y);
|
||||
} else {
|
||||
++location;
|
||||
}
|
||||
struct mMapCacheEntry* status = &cache->status[location];
|
||||
if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
|
||||
status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
|
||||
cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
|
||||
}
|
||||
unsigned tileId = status->tileId + cache->tileStart;
|
||||
if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
|
||||
tileId = 0;
|
||||
}
|
||||
const color_t* tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
|
||||
color_t* mapOut = &cache->cache[(y * stride + x) * 8];
|
||||
_cleanTile(cache, tile, mapOut, status);
|
||||
}
|
||||
}
|
||||
|
||||
const color_t* mMapCacheGetRow(struct mMapCache* cache, unsigned y) {
|
||||
size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
|
||||
return &cache->cache[y * stride];
|
||||
}
|
|
@ -409,11 +409,8 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
|
|||
}
|
||||
#endif
|
||||
ssize_t stateSize = core->stateSize(core);
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
if (vf->size(vf) < stateSize) {
|
||||
return false;
|
||||
}
|
||||
void* state = anonymousMemoryMap(stateSize);
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
if (vf->read(vf, state, stateSize) != stateSize) {
|
||||
mappedMemoryFree(state, stateSize);
|
||||
return 0;
|
||||
|
|
|
@ -34,9 +34,3 @@ M_TEST_SUITE_DEFINE(mCore,
|
|||
#endif
|
||||
cmocka_unit_test(findNullVF),
|
||||
cmocka_unit_test(findEmpty))
|
||||
|
||||
int TestRunCore(void) {
|
||||
int failures = 0;
|
||||
failures += M_TEST_SUITE_RUN(mCore);
|
||||
return failures;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,6 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
};
|
||||
core->addCoreCallbacks(core, &callbacks);
|
||||
core->setSync(core, &threadContext->impl->sync);
|
||||
core->reset(core);
|
||||
|
||||
struct mLogFilter filter;
|
||||
if (!threadContext->logger.d.filter) {
|
||||
|
@ -168,12 +167,13 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
|
||||
mCoreThreadRewindParamsChanged(threadContext);
|
||||
|
||||
_changeState(threadContext->impl, THREAD_RUNNING, true);
|
||||
|
||||
if (threadContext->startCallback) {
|
||||
threadContext->startCallback(threadContext);
|
||||
}
|
||||
|
||||
core->reset(core);
|
||||
_changeState(threadContext->impl, THREAD_RUNNING, true);
|
||||
|
||||
if (threadContext->resetCallback) {
|
||||
threadContext->resetCallback(threadContext);
|
||||
}
|
||||
|
@ -195,38 +195,58 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
}
|
||||
|
||||
int resetScheduled = 0;
|
||||
enum mCoreThreadState deferred = THREAD_RUNNING;
|
||||
MutexLock(&impl->stateMutex);
|
||||
while (impl->state > THREAD_MAX_RUNNING && impl->state < THREAD_EXITING) {
|
||||
if (impl->state == THREAD_PAUSING) {
|
||||
impl->state = THREAD_PAUSED;
|
||||
ConditionWake(&impl->stateCond);
|
||||
}
|
||||
deferred = impl->state;
|
||||
|
||||
if (impl->state == THREAD_INTERRUPTING) {
|
||||
impl->state = THREAD_INTERRUPTED;
|
||||
ConditionWake(&impl->stateCond);
|
||||
}
|
||||
if (impl->state == THREAD_RUN_ON) {
|
||||
if (threadContext->run) {
|
||||
threadContext->run(threadContext);
|
||||
}
|
||||
impl->state = impl->savedState;
|
||||
ConditionWake(&impl->stateCond);
|
||||
|
||||
if (impl->state == THREAD_PAUSING) {
|
||||
impl->state = THREAD_PAUSED;
|
||||
}
|
||||
if (impl->state == THREAD_RESETING) {
|
||||
impl->state = THREAD_RUNNING;
|
||||
resetScheduled = 1;
|
||||
}
|
||||
while (impl->state == THREAD_PAUSED || impl->state == THREAD_INTERRUPTED || impl->state == THREAD_WAITING) {
|
||||
|
||||
if (deferred >= THREAD_MIN_DEFERRED && deferred <= THREAD_MAX_DEFERRED) {
|
||||
break;
|
||||
}
|
||||
|
||||
deferred = impl->state;
|
||||
while (impl->state >= THREAD_WAITING && impl->state <= THREAD_MAX_WAITING) {
|
||||
ConditionWait(&impl->stateCond, &impl->stateMutex);
|
||||
}
|
||||
}
|
||||
MutexUnlock(&impl->stateMutex);
|
||||
if (resetScheduled) {
|
||||
switch (deferred) {
|
||||
case THREAD_PAUSING:
|
||||
if (threadContext->pauseCallback) {
|
||||
threadContext->pauseCallback(threadContext);
|
||||
}
|
||||
break;
|
||||
case THREAD_PAUSED:
|
||||
if (threadContext->unpauseCallback) {
|
||||
threadContext->unpauseCallback(threadContext);
|
||||
}
|
||||
break;
|
||||
case THREAD_RUN_ON:
|
||||
if (threadContext->run) {
|
||||
threadContext->run(threadContext);
|
||||
}
|
||||
threadContext->impl->state = threadContext->impl->savedState;
|
||||
break;
|
||||
case THREAD_RESETING:
|
||||
core->reset(core);
|
||||
if (threadContext->resetCallback) {
|
||||
threadContext->resetCallback(threadContext);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,60 +12,42 @@ void mTileCacheInit(struct mTileCache* cache) {
|
|||
cache->cache = NULL;
|
||||
cache->config = mTileCacheConfigurationFillShouldStore(0);
|
||||
cache->status = NULL;
|
||||
cache->activePalette = 0;
|
||||
memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
|
||||
cache->globalPaletteVersion = NULL;
|
||||
cache->palette = NULL;
|
||||
}
|
||||
|
||||
static void _freeCache(struct mTileCache* cache) {
|
||||
unsigned count0;
|
||||
count0 = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
|
||||
unsigned count1;
|
||||
count1 = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
|
||||
unsigned size = 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig);
|
||||
unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
|
||||
unsigned size = count0 > count1 ? count0 : count1;
|
||||
if (cache->cache) {
|
||||
mappedMemoryFree(cache->cache, 8 * 8 * 2 * tiles * size);
|
||||
mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles * size);
|
||||
cache->cache = NULL;
|
||||
}
|
||||
if (cache->status) {
|
||||
mappedMemoryFree(cache->status, tiles * size * sizeof(*cache->status));
|
||||
cache->status = NULL;
|
||||
}
|
||||
free(cache->globalPaletteVersion[0]);
|
||||
free(cache->globalPaletteVersion[1]);
|
||||
memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
|
||||
free(cache->globalPaletteVersion);
|
||||
cache->globalPaletteVersion = NULL;
|
||||
free(cache->palette);
|
||||
cache->palette = NULL;
|
||||
}
|
||||
|
||||
static void _redoCacheSize(struct mTileCache* cache) {
|
||||
if (!mTileCacheConfigurationIsShouldStore(cache->config)) {
|
||||
return;
|
||||
}
|
||||
unsigned count0 = mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
|
||||
unsigned bpp0 = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
|
||||
bpp0 = 1 << (1 << bpp0);
|
||||
if (count0) {
|
||||
count0 = 1 << count0;
|
||||
}
|
||||
unsigned count1 = mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
|
||||
unsigned bpp1 = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
|
||||
bpp1 = 1 << (1 << bpp1);
|
||||
if (count1) {
|
||||
count1 = 1 << count1;
|
||||
}
|
||||
unsigned size = count0 > count1 ? count0 : count1;
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
unsigned size = mTileCacheSystemInfoGetPaletteCount(cache->sysConfig);
|
||||
unsigned bpp = mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig);
|
||||
cache->bpp = bpp;
|
||||
bpp = 1 << (1 << bpp);
|
||||
size = 1 << size;
|
||||
cache->entriesPerTile = size;
|
||||
unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
|
||||
cache->cache = anonymousMemoryMap(8 * 8 * 2 * tiles * size);
|
||||
cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles * size);
|
||||
cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
|
||||
if (count0) {
|
||||
cache->globalPaletteVersion[0] = malloc(count0 * bpp0 * sizeof(*cache->globalPaletteVersion[0]));
|
||||
}
|
||||
if (count1) {
|
||||
cache->globalPaletteVersion[1] = malloc(count1 * bpp1 * sizeof(*cache->globalPaletteVersion[1]));
|
||||
}
|
||||
cache->globalPaletteVersion = malloc(size * sizeof(*cache->globalPaletteVersion));
|
||||
cache->palette = malloc(size * bpp * sizeof(*cache->palette));
|
||||
}
|
||||
|
||||
void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
|
||||
|
@ -74,9 +56,11 @@ void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration confi
|
|||
_redoCacheSize(cache);
|
||||
}
|
||||
|
||||
void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config) {
|
||||
void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config, uint32_t tileBase, uint32_t paletteBase) {
|
||||
_freeCache(cache);
|
||||
cache->sysConfig = config;
|
||||
cache->tileBase = tileBase;
|
||||
cache->paletteBase = paletteBase;
|
||||
_redoCacheSize(cache);
|
||||
}
|
||||
|
||||
|
@ -85,40 +69,41 @@ void mTileCacheDeinit(struct mTileCache* cache) {
|
|||
}
|
||||
|
||||
void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) {
|
||||
if (address < cache->tileBase) {
|
||||
return;
|
||||
}
|
||||
address -= cache->tileBase;
|
||||
unsigned bpp = cache->bpp + 3;
|
||||
unsigned count = cache->entriesPerTile;
|
||||
address >>= bpp;
|
||||
if (address >= mTileCacheSystemInfoGetMaxTiles(cache->sysConfig)) {
|
||||
return;
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
cache->status[(address >> bpp) * count + i].vramClean = 0;
|
||||
++cache->status[(address >> bpp) * count + i].vramVersion;
|
||||
cache->status[address * count + i].vramClean = 0;
|
||||
++cache->status[address * count + i].vramVersion;
|
||||
}
|
||||
}
|
||||
|
||||
void mTileCacheWritePalette(struct mTileCache* cache, uint32_t address) {
|
||||
if (cache->globalPaletteVersion[0]) {
|
||||
++cache->globalPaletteVersion[0][address >> 1];
|
||||
void mTileCacheWritePalette(struct mTileCache* cache, uint32_t entry, color_t color) {
|
||||
if (entry < cache->paletteBase) {
|
||||
return;
|
||||
}
|
||||
if (cache->globalPaletteVersion[1]) {
|
||||
++cache->globalPaletteVersion[1][address >> 1];
|
||||
entry -= cache->paletteBase;
|
||||
unsigned maxEntry = (1 << (1 << cache->bpp)) * cache->entriesPerTile;
|
||||
if (entry >= maxEntry) {
|
||||
return;
|
||||
}
|
||||
cache->palette[entry] = color;
|
||||
entry >>= (1 << mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig));
|
||||
++cache->globalPaletteVersion[entry];
|
||||
}
|
||||
|
||||
void mTileCacheSetPalette(struct mTileCache* cache, int palette) {
|
||||
cache->activePalette = palette;
|
||||
if (palette == 0) {
|
||||
cache->bpp = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
|
||||
cache->count = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
|
||||
} else {
|
||||
cache->bpp = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
|
||||
cache->count = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
|
||||
}
|
||||
cache->entries = 1 << (1 << cache->bpp);
|
||||
}
|
||||
|
||||
static void _regenerateTile4(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
static void _regenerateTile4(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
uint8_t* start = (uint8_t*) &cache->vram[tileId << 3];
|
||||
paletteId <<= 2;
|
||||
uint16_t* palette = &cache->palette[paletteId];
|
||||
color_t* palette = &cache->palette[paletteId];
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
uint8_t tileDataLower = start[0];
|
||||
|
@ -126,108 +111,114 @@ static void _regenerateTile4(struct mTileCache* cache, uint16_t* tile, unsigned
|
|||
start += 2;
|
||||
int pixel;
|
||||
pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
|
||||
tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
|
||||
tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
|
||||
tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
|
||||
tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
|
||||
tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
|
||||
tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
|
||||
tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
|
||||
tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
tile += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void _regenerateTile16(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
static void _regenerateTile16(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
uint32_t* start = (uint32_t*) &cache->vram[tileId << 4];
|
||||
paletteId <<= 4;
|
||||
uint16_t* palette = &cache->palette[paletteId];
|
||||
color_t* palette = &cache->palette[paletteId];
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
uint32_t line = *start;
|
||||
++start;
|
||||
int pixel;
|
||||
pixel = line & 0xF;
|
||||
tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 4) & 0xF;
|
||||
tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 8) & 0xF;
|
||||
tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 12) & 0xF;
|
||||
tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 16) & 0xF;
|
||||
tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 20) & 0xF;
|
||||
tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 24) & 0xF;
|
||||
tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 28) & 0xF;
|
||||
tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
tile += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void _regenerateTile256(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
static void _regenerateTile256(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
|
||||
uint32_t* start = (uint32_t*) &cache->vram[tileId << 5];
|
||||
paletteId <<= 8;
|
||||
uint16_t* palette = &cache->palette[paletteId];
|
||||
color_t* palette = &cache->palette[paletteId];
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
uint32_t line = *start;
|
||||
++start;
|
||||
int pixel;
|
||||
pixel = line & 0xFF;
|
||||
tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 8) & 0xFF;
|
||||
tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 16) & 0xFF;
|
||||
tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 24) & 0xFF;
|
||||
tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
|
||||
line = *start;
|
||||
++start;
|
||||
pixel = line & 0xFF;
|
||||
tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 8) & 0xFF;
|
||||
tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 16) & 0xFF;
|
||||
tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
pixel = (line >> 24) & 0xFF;
|
||||
tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
|
||||
tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
|
||||
tile += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
|
||||
static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
|
||||
if (mTileCacheConfigurationIsShouldStore(cache->config)) {
|
||||
unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
|
||||
#ifndef NDEBUG
|
||||
if (tileId >= tiles) {
|
||||
abort();
|
||||
}
|
||||
if (paletteId >= 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
return &cache->cache[(tileId + paletteId * tiles) << 6];
|
||||
} else {
|
||||
return cache->temporaryTile;
|
||||
}
|
||||
}
|
||||
|
||||
const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
|
||||
unsigned cPaletteId = cache->activePalette;
|
||||
const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
|
||||
unsigned count = cache->entriesPerTile;
|
||||
unsigned bpp = cache->bpp;
|
||||
struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
|
||||
struct mTileCacheEntry desiredStatus = {
|
||||
.paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
|
||||
.paletteVersion = cache->globalPaletteVersion[paletteId],
|
||||
.vramVersion = status->vramVersion,
|
||||
.vramClean = 1,
|
||||
.paletteId = paletteId,
|
||||
.activePalette = cPaletteId
|
||||
.paletteId = paletteId
|
||||
};
|
||||
uint16_t* tile = _tileLookup(cache, tileId, paletteId);
|
||||
color_t* tile = _tileLookup(cache, tileId, paletteId);
|
||||
if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
|
||||
switch (bpp) {
|
||||
case 0:
|
||||
|
@ -247,19 +238,17 @@ const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, uns
|
|||
return tile;
|
||||
}
|
||||
|
||||
const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
|
||||
unsigned cPaletteId = cache->activePalette;
|
||||
const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
|
||||
unsigned count = cache->entriesPerTile;
|
||||
unsigned bpp = cache->bpp;
|
||||
struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
|
||||
struct mTileCacheEntry desiredStatus = {
|
||||
.paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
|
||||
.paletteVersion = cache->globalPaletteVersion[paletteId],
|
||||
.vramVersion = status->vramVersion,
|
||||
.vramClean = 1,
|
||||
.paletteId = paletteId,
|
||||
.activePalette = cPaletteId
|
||||
.paletteId = paletteId
|
||||
};
|
||||
uint16_t* tile = NULL;
|
||||
color_t* tile = NULL;
|
||||
if (memcmp(status, &desiredStatus, sizeof(*status))) {
|
||||
tile = _tileLookup(cache, tileId, paletteId);
|
||||
switch (bpp) {
|
||||
|
@ -284,26 +273,10 @@ const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileC
|
|||
return tile;
|
||||
}
|
||||
|
||||
const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId) {
|
||||
unsigned bpp = cache->bpp;
|
||||
switch (bpp) {
|
||||
case 0:
|
||||
return NULL;
|
||||
default:
|
||||
return (uint8_t*) &cache->vram[tileId << (2 + bpp)];
|
||||
}
|
||||
const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
|
||||
return &cache->palette[paletteId << (1 << cache->bpp)];
|
||||
}
|
||||
|
||||
const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
|
||||
unsigned bpp = cache->bpp;
|
||||
switch (bpp) {
|
||||
default:
|
||||
return NULL;
|
||||
case 1:
|
||||
return &cache->palette[paletteId << 2];
|
||||
case 2:
|
||||
return &cache->palette[paletteId << 4];
|
||||
case 3:
|
||||
return &cache->palette[paletteId << 8];
|
||||
}
|
||||
const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId) {
|
||||
return &cache->vram[tileId << (cache->bpp + 2)];
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextEvent) {
|
||||
timing->root = NULL;
|
||||
timing->reroot = NULL;
|
||||
timing->masterCycles = 0;
|
||||
timing->relativeCycles = relativeCycles;
|
||||
timing->nextEvent = nextEvent;
|
||||
|
@ -17,6 +18,7 @@ void mTimingDeinit(struct mTiming* timing) {
|
|||
|
||||
void mTimingClear(struct mTiming* timing) {
|
||||
timing->root = NULL;
|
||||
timing->reroot = NULL;
|
||||
timing->masterCycles = 0;
|
||||
}
|
||||
|
||||
|
@ -77,6 +79,11 @@ int32_t mTimingTick(struct mTiming* timing, int32_t cycles) {
|
|||
timing->root = next->next;
|
||||
next->callback(timing, next->context, -nextWhen);
|
||||
}
|
||||
if (timing->reroot) {
|
||||
timing->root = timing->reroot;
|
||||
timing->reroot = NULL;
|
||||
*timing->nextEvent = mTimingNextEvent(timing);
|
||||
}
|
||||
return *timing->nextEvent;
|
||||
}
|
||||
|
||||
|
|
|
@ -687,8 +687,16 @@ void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* contex
|
|||
|
||||
void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core) {
|
||||
_readHeader(context);
|
||||
if (core && core->stateSize(core) == context->initialStateSize) {
|
||||
core->loadState(core, context->initialState);
|
||||
if (core) {
|
||||
size_t size = core->stateSize(core);
|
||||
if (size <= context->initialStateSize) {
|
||||
core->loadState(core, context->initialState);
|
||||
} else {
|
||||
void* extendedState = anonymousMemoryMap(size);
|
||||
memcpy(extendedState, context->initialState, context->initialStateSize);
|
||||
core->loadState(core, extendedState);
|
||||
mappedMemoryFree(extendedState, size);
|
||||
}
|
||||
}
|
||||
|
||||
off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR);
|
||||
|
|
|
@ -25,7 +25,7 @@ const int GB_AUDIO_VOLUME_MAX = 0x100;
|
|||
|
||||
static bool _writeSweep(struct GBAudioSweep* sweep, uint8_t value);
|
||||
static void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value);
|
||||
static bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value);
|
||||
static bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudioStyle style);
|
||||
|
||||
static void _resetSweep(struct GBAudioSweep* sweep);
|
||||
static bool _resetEnvelope(struct GBAudioEnvelope* sweep);
|
||||
|
@ -153,6 +153,10 @@ void GBAudioReset(struct GBAudio* audio) {
|
|||
audio->playingCh2 = false;
|
||||
audio->playingCh3 = false;
|
||||
audio->playingCh4 = false;
|
||||
if (audio->p && (audio->p->model == GB_MODEL_DMG || audio->p->model == GB_MODEL_CGB)) {
|
||||
audio->playingCh1 = true;
|
||||
*audio->nr52 |= 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
|
||||
|
@ -178,7 +182,7 @@ void GBAudioWriteNR11(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBAudioWriteNR12(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch1.envelope, value)) {
|
||||
if (!_writeEnvelope(&audio->ch1.envelope, value, audio->style)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch1Event);
|
||||
audio->playingCh1 = false;
|
||||
*audio->nr52 &= ~0x0001;
|
||||
|
@ -236,7 +240,7 @@ void GBAudioWriteNR21(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBAudioWriteNR22(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch2.envelope, value)) {
|
||||
if (!_writeEnvelope(&audio->ch2.envelope, value, audio->style)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch2Event);
|
||||
audio->playingCh2 = false;
|
||||
*audio->nr52 &= ~0x0002;
|
||||
|
@ -354,7 +358,7 @@ void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch4.envelope, value)) {
|
||||
if (!_writeEnvelope(&audio->ch4.envelope, value, audio->style)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch4Event);
|
||||
audio->playingCh4 = false;
|
||||
*audio->nr52 &= ~0x0008;
|
||||
|
@ -696,17 +700,16 @@ void _writeDuty(struct GBAudioEnvelope* envelope, uint8_t value) {
|
|||
envelope->duty = GBAudioRegisterDutyGetDuty(value);
|
||||
}
|
||||
|
||||
bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) {
|
||||
bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudioStyle style) {
|
||||
envelope->stepTime = GBAudioRegisterSweepGetStepTime(value);
|
||||
envelope->direction = GBAudioRegisterSweepGetDirection(value);
|
||||
envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);
|
||||
if (!envelope->stepTime) {
|
||||
if (style == GB_AUDIO_DMG && !envelope->stepTime) {
|
||||
// TODO: Improve "zombie" mode
|
||||
++envelope->currentVolume;
|
||||
envelope->currentVolume &= 0xF;
|
||||
}
|
||||
_updateEnvelopeDead(envelope);
|
||||
envelope->nextStep = envelope->stepTime;
|
||||
return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
|
||||
static const struct mCoreChannelInfo _GBVideoLayers[] = {
|
||||
{ 0, "bg", "Background", NULL },
|
||||
{ 1, "obj", "Objects", NULL },
|
||||
{ 2, "win", "Window", NULL },
|
||||
{ 1, "bgwin", "Window", NULL },
|
||||
{ 2, "obj", "Objects", NULL },
|
||||
};
|
||||
|
||||
static const struct mCoreChannelInfo _GBAudioChannels[] = {
|
||||
|
@ -188,9 +188,38 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
|
|||
if (mCoreConfigGetIntValue(config, "gb.pal[3]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 3, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[4]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 4, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[5]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 5, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[6]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 6, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[7]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 7, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[8]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 8, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[9]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 9, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[10]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 10, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(config, "gb.pal[11]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 11, color);
|
||||
}
|
||||
|
||||
mCoreConfigCopyValue(&core->config, config, "gb.bios");
|
||||
mCoreConfigCopyValue(&core->config, config, "sgb.bios");
|
||||
mCoreConfigCopyValue(&core->config, config, "gbc.bios");
|
||||
mCoreConfigCopyValue(&core->config, config, "gb.model");
|
||||
mCoreConfigCopyValue(&core->config, config, "sgb.model");
|
||||
mCoreConfigCopyValue(&core->config, config, "cgb.model");
|
||||
mCoreConfigCopyValue(&core->config, config, "sgb.borders");
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
|
@ -199,9 +228,14 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
|
|||
}
|
||||
|
||||
static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
||||
UNUSED(core);
|
||||
*width = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = GB_VIDEO_VERTICAL_PIXELS;
|
||||
struct GB* gb = core->board;
|
||||
if (gb && (gb->model != GB_MODEL_SGB || !gb->video.sgbBorders)) {
|
||||
*width = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = GB_VIDEO_VERTICAL_PIXELS;
|
||||
} else {
|
||||
*width = 256;
|
||||
*height = 224;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
|
||||
|
@ -330,6 +364,25 @@ static void _GBCoreReset(struct mCore* core) {
|
|||
}
|
||||
}
|
||||
|
||||
const char* modelGB = mCoreConfigGetValue(&core->config, "gb.model");
|
||||
const char* modelCGB = mCoreConfigGetValue(&core->config, "cgb.model");
|
||||
const char* modelSGB = mCoreConfigGetValue(&core->config, "sgb.model");
|
||||
if (modelGB || modelCGB || modelSGB) {
|
||||
GBDetectModel(gb);
|
||||
if (gb->model == GB_MODEL_DMG && modelGB) {
|
||||
gb->model = GBNameToModel(modelGB);
|
||||
} else if (gb->model == GB_MODEL_CGB && modelCGB) {
|
||||
gb->model = GBNameToModel(modelCGB);
|
||||
} else if (gb->model == GB_MODEL_SGB && modelSGB) {
|
||||
gb->model = GBNameToModel(modelSGB);
|
||||
}
|
||||
}
|
||||
|
||||
int fakeBool;
|
||||
if (mCoreConfigGetIntValue(&core->config, "sgb.borders", &fakeBool)) {
|
||||
gb->video.sgbBorders = fakeBool;
|
||||
}
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
if (!gb->biosVf && core->opts.useBios) {
|
||||
struct VFile* bios = NULL;
|
||||
|
@ -349,9 +402,13 @@ static void _GBCoreReset(struct mCore* core) {
|
|||
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
case GB_MODEL_SGB: // TODO
|
||||
case GB_MODEL_MGB: // TODO
|
||||
configPath = mCoreConfigGetValue(&core->config, "gb.bios");
|
||||
break;
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_SGB2: // TODO
|
||||
configPath = mCoreConfigGetValue(&core->config, "sgb.bios");
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
case GB_MODEL_AGB:
|
||||
configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
|
||||
|
@ -374,9 +431,13 @@ static void _GBCoreReset(struct mCore* core) {
|
|||
mCoreConfigDirectory(path, PATH_MAX);
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
case GB_MODEL_SGB: // TODO
|
||||
case GB_MODEL_MGB: // TODO
|
||||
strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
|
||||
break;
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_SGB2: // TODO
|
||||
strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path));
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
case GB_MODEL_AGB:
|
||||
strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
|
||||
|
@ -570,7 +631,9 @@ size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlo
|
|||
const struct GB* gb = core->board;
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
case GB_MODEL_MGB:
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_SGB2:
|
||||
default:
|
||||
*blocks = _GBMemoryBlocks;
|
||||
return sizeof(_GBMemoryBlocks) / sizeof(*_GBMemoryBlocks);
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gb/renderers/proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
|
||||
#define BUFFER_OAM 1
|
||||
#define BUFFER_SGB 2
|
||||
|
||||
static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
|
||||
static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
|
||||
static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
|
||||
static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
|
||||
static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
|
||||
|
@ -30,6 +32,7 @@ void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GB
|
|||
renderer->d.init = GBVideoProxyRendererInit;
|
||||
renderer->d.deinit = GBVideoProxyRendererDeinit;
|
||||
renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
|
||||
renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket;
|
||||
renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
|
||||
renderer->d.writePalette = GBVideoProxyRendererWritePalette;
|
||||
|
@ -93,12 +96,12 @@ void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRender
|
|||
mVideoLoggerRendererDeinit(renderer->logger);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||
void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
|
||||
_init(proxyRenderer);
|
||||
|
||||
proxyRenderer->backend->init(proxyRenderer->backend, model);
|
||||
proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
|
||||
|
@ -111,6 +114,7 @@ void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
|
|||
|
||||
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = logger->context;
|
||||
uint8_t sgbPacket[16];
|
||||
switch (item->type) {
|
||||
case DIRTY_REGISTER:
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||
|
@ -154,6 +158,11 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
|
|||
return false;
|
||||
}
|
||||
logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true);
|
||||
break;
|
||||
case BUFFER_SGB:
|
||||
logger->readData(logger, sgbPacket, 16, true);
|
||||
proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DIRTY_FLUSH:
|
||||
|
@ -179,6 +188,14 @@ uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer,
|
|||
return value;
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
|
||||
}
|
||||
mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
|
||||
|
@ -186,7 +203,7 @@ void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t ad
|
|||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +214,7 @@ void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int addr
|
|||
proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
mCacheSetWritePalette(renderer->cache, address, mColorFrom555(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
177
src/gb/gb.c
177
src/gb/gb.c
|
@ -28,6 +28,8 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
|
|||
|
||||
#define DMG_BIOS_CHECKSUM 0xC2F5CC97
|
||||
#define DMG_2_BIOS_CHECKSUM 0x59C8598E
|
||||
#define MGB_BIOS_CHECKSUM 0xE6920754
|
||||
#define SGB_BIOS_CHECKSUM 0xEC8A83B9
|
||||
#define CGB_BIOS_CHECKSUM 0x41884E46
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GB, "GB", "gb");
|
||||
|
@ -37,6 +39,7 @@ static void GBDeinit(struct mCPUComponent* component);
|
|||
static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
|
||||
static void GBProcessEvents(struct LR35902Core* cpu);
|
||||
static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
|
||||
static uint16_t GBIRQVector(struct LR35902Core* cpu);
|
||||
static void GBIllegal(struct LR35902Core* cpu);
|
||||
static void GBStop(struct LR35902Core* cpu);
|
||||
|
||||
|
@ -162,7 +165,6 @@ void GBResizeSram(struct GB* gb, size_t size) {
|
|||
if (gb->memory.sram && size <= gb->sramSize) {
|
||||
return;
|
||||
}
|
||||
mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (vf) {
|
||||
if (vf == gb->sramRealVf) {
|
||||
|
@ -220,7 +222,7 @@ void GBResizeSram(struct GB* gb, size_t size) {
|
|||
|
||||
void GBSramClean(struct GB* gb, uint32_t frameCount) {
|
||||
// TODO: Share with GBASavedataClean
|
||||
if (!gb->sramVf || gb->sramVf != gb->sramRealVf) {
|
||||
if (!gb->sramVf) {
|
||||
return;
|
||||
}
|
||||
if (gb->sramDirty & GB_SRAM_DIRT_NEW) {
|
||||
|
@ -230,6 +232,9 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) {
|
|||
gb->sramDirty |= GB_SRAM_DIRT_SEEN;
|
||||
}
|
||||
} else if ((gb->sramDirty & GB_SRAM_DIRT_SEEN) && frameCount - gb->sramDirtAge > CLEANUP_THRESHOLD) {
|
||||
if (gb->sramMaskWriteback) {
|
||||
GBSavedataUnmask(gb);
|
||||
}
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
GBMBCRTCWrite(gb);
|
||||
}
|
||||
|
@ -259,7 +264,9 @@ void GBSavedataUnmask(struct GB* gb) {
|
|||
gb->sramVf = gb->sramRealVf;
|
||||
gb->memory.sram = gb->sramVf->map(gb->sramVf, gb->sramSize, MAP_WRITE);
|
||||
if (gb->sramMaskWriteback) {
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
vf->read(vf, gb->memory.sram, gb->sramSize);
|
||||
gb->sramMaskWriteback = false;
|
||||
}
|
||||
vf->close(vf);
|
||||
}
|
||||
|
@ -287,6 +294,7 @@ void GBUnloadROM(struct GB* gb) {
|
|||
gb->memory.mbcType = GB_MBC_AUTODETECT;
|
||||
gb->isPristine = false;
|
||||
|
||||
gb->sramMaskWriteback = false;
|
||||
GBSavedataUnmask(gb);
|
||||
GBSramDeinit(gb);
|
||||
if (gb->sramRealVf) {
|
||||
|
@ -364,6 +372,7 @@ void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
|
|||
irqh->reset = GBReset;
|
||||
irqh->processEvents = GBProcessEvents;
|
||||
irqh->setInterrupts = GBSetInterrupts;
|
||||
irqh->irqVector = GBIRQVector;
|
||||
irqh->hitIllegal = GBIllegal;
|
||||
irqh->stop = GBStop;
|
||||
irqh->halt = GBHalt;
|
||||
|
@ -384,6 +393,8 @@ bool GBIsBIOS(struct VFile* vf) {
|
|||
switch (_GBBiosCRC32(vf)) {
|
||||
case DMG_BIOS_CHECKSUM:
|
||||
case DMG_2_BIOS_CHECKSUM:
|
||||
case MGB_BIOS_CHECKSUM:
|
||||
case SGB_BIOS_CHECKSUM:
|
||||
case CGB_BIOS_CHECKSUM:
|
||||
return true;
|
||||
default:
|
||||
|
@ -423,24 +434,63 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
cpu->b = 0;
|
||||
cpu->d = 0;
|
||||
|
||||
gb->timer.internalDiv = 0;
|
||||
int nextDiv = 0;
|
||||
if (!gb->biosVf) {
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
// TODO: SGB
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_AUTODETECT: // Silence warnings
|
||||
gb->model = GB_MODEL_DMG;
|
||||
case GB_MODEL_DMG:
|
||||
cpu->a = 1;
|
||||
cpu->f.packed = 0xB0;
|
||||
cpu->c = 0x13;
|
||||
cpu->e = 0xD8;
|
||||
cpu->h = 1;
|
||||
cpu->l = 0x4D;
|
||||
gb->timer.internalDiv = 0x2AF3;
|
||||
gb->timer.internalDiv = 0xABC;
|
||||
nextDiv = 4;
|
||||
break;
|
||||
case GB_MODEL_SGB:
|
||||
cpu->a = 1;
|
||||
cpu->f.packed = 0x00;
|
||||
cpu->c = 0x14;
|
||||
cpu->e = 0x00;
|
||||
cpu->h = 0xC0;
|
||||
cpu->l = 0x60;
|
||||
gb->timer.internalDiv = 0xABC;
|
||||
nextDiv = 4;
|
||||
break;
|
||||
case GB_MODEL_MGB:
|
||||
cpu->a = 0xFF;
|
||||
cpu->f.packed = 0xB0;
|
||||
cpu->c = 0x13;
|
||||
cpu->e = 0xD8;
|
||||
cpu->h = 1;
|
||||
cpu->l = 0x4D;
|
||||
gb->timer.internalDiv = 0xABC;
|
||||
nextDiv = 4;
|
||||
break;
|
||||
case GB_MODEL_SGB2:
|
||||
cpu->a = 0xFF;
|
||||
cpu->f.packed = 0x00;
|
||||
cpu->c = 0x14;
|
||||
cpu->e = 0x00;
|
||||
cpu->h = 0xC0;
|
||||
cpu->l = 0x60;
|
||||
gb->timer.internalDiv = 0xABC;
|
||||
nextDiv = 4;
|
||||
break;
|
||||
case GB_MODEL_AGB:
|
||||
cpu->a = 0x11;
|
||||
cpu->b = 1;
|
||||
// Fall through
|
||||
cpu->f.packed = 0x00;
|
||||
cpu->c = 0;
|
||||
cpu->e = 0x08;
|
||||
cpu->h = 0;
|
||||
cpu->l = 0x7C;
|
||||
gb->timer.internalDiv = 0x1EA;
|
||||
nextDiv = 0xC;
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
cpu->a = 0x11;
|
||||
cpu->f.packed = 0x80;
|
||||
|
@ -448,7 +498,8 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
cpu->e = 0x08;
|
||||
cpu->h = 0;
|
||||
cpu->l = 0x7C;
|
||||
gb->timer.internalDiv = 0x7A8;
|
||||
gb->timer.internalDiv = 0x1EA;
|
||||
nextDiv = 0xC;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -467,15 +518,19 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
gb->yankedRomSize = 0;
|
||||
}
|
||||
|
||||
gb->sgbBit = -1;
|
||||
gb->currentSgbBits = 0;
|
||||
memset(gb->sgbPacket, 0, sizeof(gb->sgbPacket));
|
||||
|
||||
mTimingClear(&gb->timing);
|
||||
|
||||
GBMemoryReset(gb);
|
||||
GBVideoReset(&gb->video);
|
||||
GBTimerReset(&gb->timer);
|
||||
mTimingSchedule(&gb->timing, &gb->timer.event, GB_DMG_DIV_PERIOD);
|
||||
mTimingSchedule(&gb->timing, &gb->timer.event, nextDiv);
|
||||
|
||||
GBAudioReset(&gb->audio);
|
||||
GBIOReset(gb);
|
||||
GBAudioReset(&gb->audio);
|
||||
GBSIOReset(&gb->sio);
|
||||
|
||||
GBSavedataUnmask(gb);
|
||||
|
@ -491,6 +546,12 @@ void GBDetectModel(struct GB* gb) {
|
|||
case DMG_2_BIOS_CHECKSUM:
|
||||
gb->model = GB_MODEL_DMG;
|
||||
break;
|
||||
case MGB_BIOS_CHECKSUM:
|
||||
gb->model = GB_MODEL_MGB;
|
||||
break;
|
||||
case SGB_BIOS_CHECKSUM:
|
||||
gb->model = GB_MODEL_SGB;
|
||||
break;
|
||||
case CGB_BIOS_CHECKSUM:
|
||||
gb->model = GB_MODEL_CGB;
|
||||
break;
|
||||
|
@ -503,6 +564,8 @@ void GBDetectModel(struct GB* gb) {
|
|||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
if (cart->cgb & 0x80) {
|
||||
gb->model = GB_MODEL_CGB;
|
||||
} else if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) {
|
||||
gb->model = GB_MODEL_SGB;
|
||||
} else {
|
||||
gb->model = GB_MODEL_DMG;
|
||||
}
|
||||
|
@ -514,6 +577,10 @@ void GBDetectModel(struct GB* gb) {
|
|||
case GB_MODEL_AUTODETECT: //Silence warnings
|
||||
gb->audio.style = GB_AUDIO_DMG;
|
||||
break;
|
||||
case GB_MODEL_MGB:
|
||||
case GB_MODEL_SGB2:
|
||||
gb->audio.style = GB_AUDIO_MGB;
|
||||
break;
|
||||
case GB_MODEL_AGB:
|
||||
case GB_MODEL_CGB:
|
||||
gb->audio.style = GB_AUDIO_CGB;
|
||||
|
@ -531,31 +598,7 @@ void GBUpdateIRQs(struct GB* gb) {
|
|||
if (!gb->memory.ime || gb->cpu->irqPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (irqs & (1 << GB_IRQ_VBLANK)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_LCDSTAT)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_TIMER)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_SIO)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_KEYPAD)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
|
||||
}
|
||||
LR35902RaiseIRQ(gb->cpu);
|
||||
}
|
||||
|
||||
void GBProcessEvents(struct LR35902Core* cpu) {
|
||||
|
@ -598,6 +641,33 @@ void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
uint16_t GBIRQVector(struct LR35902Core* cpu) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
int irqs = gb->memory.ie & gb->memory.io[REG_IF];
|
||||
|
||||
if (irqs & (1 << GB_IRQ_VBLANK)) {
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
|
||||
return GB_VECTOR_VBLANK;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_LCDSTAT)) {
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
|
||||
return GB_VECTOR_LCDSTAT;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_TIMER)) {
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
|
||||
return GB_VECTOR_TIMER;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_SIO)) {
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
|
||||
return GB_VECTOR_SIO;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_KEYPAD)) {
|
||||
gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
|
||||
return GB_VECTOR_KEYPAD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
|
@ -721,3 +791,40 @@ void GBFrameEnded(struct GB* gb) {
|
|||
|
||||
GBTestKeypadIRQ(gb);
|
||||
}
|
||||
|
||||
enum GBModel GBNameToModel(const char* model) {
|
||||
if (strcasecmp(model, "DMG") == 0) {
|
||||
return GB_MODEL_DMG;
|
||||
} else if (strcasecmp(model, "CGB") == 0) {
|
||||
return GB_MODEL_CGB;
|
||||
} else if (strcasecmp(model, "AGB") == 0) {
|
||||
return GB_MODEL_AGB;
|
||||
} else if (strcasecmp(model, "SGB") == 0) {
|
||||
return GB_MODEL_SGB;
|
||||
} else if (strcasecmp(model, "MGB") == 0) {
|
||||
return GB_MODEL_MGB;
|
||||
} else if (strcasecmp(model, "SGB2") == 0) {
|
||||
return GB_MODEL_SGB2;
|
||||
}
|
||||
return GB_MODEL_AUTODETECT;
|
||||
}
|
||||
|
||||
const char* GBModelToName(enum GBModel model) {
|
||||
switch (model) {
|
||||
case GB_MODEL_DMG:
|
||||
return "DMG";
|
||||
case GB_MODEL_SGB:
|
||||
return "SGB";
|
||||
case GB_MODEL_MGB:
|
||||
return "MGB";
|
||||
case GB_MODEL_SGB2:
|
||||
return "SGB2";
|
||||
case GB_MODEL_CGB:
|
||||
return "CGB";
|
||||
case GB_MODEL_AGB:
|
||||
return "AGB";
|
||||
default:
|
||||
case GB_MODEL_AUTODETECT:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
84
src/gb/io.c
84
src/gb/io.c
|
@ -105,6 +105,33 @@ static const uint8_t _registerMask[] = {
|
|||
[REG_IE] = 0xE0,
|
||||
};
|
||||
|
||||
static void _writeSGBBits(struct GB* gb, int bits) {
|
||||
if (!bits) {
|
||||
gb->sgbBit = 0;
|
||||
memset(gb->sgbPacket, 0, sizeof(gb->sgbPacket));
|
||||
}
|
||||
if (bits == gb->currentSgbBits) {
|
||||
return;
|
||||
}
|
||||
gb->currentSgbBits = bits;
|
||||
if (gb->sgbBit == 128 && bits == 2) {
|
||||
GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket);
|
||||
++gb->sgbBit;
|
||||
}
|
||||
if (gb->sgbBit >= 128) {
|
||||
return;
|
||||
}
|
||||
switch (bits) {
|
||||
case 1:
|
||||
gb->sgbPacket[gb->sgbBit >> 3] |= 1 << (gb->sgbBit & 7);
|
||||
// Fall through
|
||||
case 2:
|
||||
++gb->sgbBit;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GBIOInit(struct GB* gb) {
|
||||
memset(gb->memory.io, 0, sizeof(gb->memory.io));
|
||||
}
|
||||
|
@ -117,19 +144,19 @@ void GBIOReset(struct GB* gb) {
|
|||
GBIOWrite(gb, REG_TAC, 0);
|
||||
GBIOWrite(gb, REG_IF, 1);
|
||||
GBIOWrite(gb, REG_NR52, 0xF1);
|
||||
GBIOWrite(gb, REG_NR14, 0xBF);
|
||||
GBIOWrite(gb, REG_NR14, 0x3F);
|
||||
GBIOWrite(gb, REG_NR10, 0x80);
|
||||
GBIOWrite(gb, REG_NR11, 0xBF);
|
||||
GBIOWrite(gb, REG_NR12, 0xF3);
|
||||
GBIOWrite(gb, REG_NR13, 0xF3);
|
||||
GBIOWrite(gb, REG_NR24, 0xBF);
|
||||
GBIOWrite(gb, REG_NR24, 0x3F);
|
||||
GBIOWrite(gb, REG_NR21, 0x3F);
|
||||
GBIOWrite(gb, REG_NR22, 0x00);
|
||||
GBIOWrite(gb, REG_NR34, 0xBF);
|
||||
GBIOWrite(gb, REG_NR34, 0x3F);
|
||||
GBIOWrite(gb, REG_NR30, 0x7F);
|
||||
GBIOWrite(gb, REG_NR31, 0xFF);
|
||||
GBIOWrite(gb, REG_NR32, 0x9F);
|
||||
GBIOWrite(gb, REG_NR44, 0xBF);
|
||||
GBIOWrite(gb, REG_NR44, 0x3F);
|
||||
GBIOWrite(gb, REG_NR41, 0xFF);
|
||||
GBIOWrite(gb, REG_NR42, 0x00);
|
||||
GBIOWrite(gb, REG_NR43, 0x00);
|
||||
|
@ -140,19 +167,26 @@ void GBIOReset(struct GB* gb) {
|
|||
GBIOWrite(gb, REG_SCX, 0x00);
|
||||
GBIOWrite(gb, REG_LYC, 0x00);
|
||||
GBIOWrite(gb, REG_BGP, 0xFC);
|
||||
GBIOWrite(gb, REG_OBP0, 0xFF);
|
||||
GBIOWrite(gb, REG_OBP1, 0xFF);
|
||||
if (gb->model < GB_MODEL_CGB) {
|
||||
GBIOWrite(gb, REG_OBP0, 0xFF);
|
||||
GBIOWrite(gb, REG_OBP1, 0xFF);
|
||||
}
|
||||
GBIOWrite(gb, REG_WY, 0x00);
|
||||
GBIOWrite(gb, REG_WX, 0x00);
|
||||
GBIOWrite(gb, REG_VBK, 0);
|
||||
GBIOWrite(gb, REG_BCPS, 0);
|
||||
GBIOWrite(gb, REG_OCPS, 0);
|
||||
GBIOWrite(gb, REG_SVBK, 1);
|
||||
GBIOWrite(gb, REG_HDMA1, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA2, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA3, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA4, 0xFF);
|
||||
gb->memory.io[REG_HDMA5] = 0xFF;
|
||||
if (gb->model >= GB_MODEL_CGB) {
|
||||
GBIOWrite(gb, REG_JOYP, 0xFF);
|
||||
GBIOWrite(gb, REG_VBK, 0);
|
||||
GBIOWrite(gb, REG_BCPS, 0);
|
||||
GBIOWrite(gb, REG_OCPS, 0);
|
||||
GBIOWrite(gb, REG_SVBK, 1);
|
||||
GBIOWrite(gb, REG_HDMA1, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA2, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA3, 0xFF);
|
||||
GBIOWrite(gb, REG_HDMA4, 0xFF);
|
||||
gb->memory.io[REG_HDMA5] = 0xFF;
|
||||
} else if (gb->model == GB_MODEL_SGB) {
|
||||
GBIOWrite(gb, REG_JOYP, 0xFF);
|
||||
}
|
||||
GBIOWrite(gb, REG_IE, 0x00);
|
||||
}
|
||||
|
||||
|
@ -341,6 +375,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
|||
}
|
||||
break;
|
||||
case REG_JOYP:
|
||||
if (gb->model == GB_MODEL_SGB) {
|
||||
_writeSGBBits(gb, (value >> 4) & 3);
|
||||
}
|
||||
break;
|
||||
case REG_TIMA:
|
||||
case REG_TMA:
|
||||
// Handled transparently by the registers
|
||||
|
@ -386,6 +424,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
|||
free(gb->memory.romBase);
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
}
|
||||
if (gb->model >= GB_MODEL_CGB && gb->memory.io[0x6C]) {
|
||||
gb->model = GB_MODEL_DMG;
|
||||
GBVideoDisableCGB(&gb->video);
|
||||
}
|
||||
break;
|
||||
case REG_IE:
|
||||
gb->memory.ie = value;
|
||||
|
@ -453,7 +495,8 @@ static uint8_t _readKeys(struct GB* gb) {
|
|||
uint8_t keys = *gb->keySource;
|
||||
switch (gb->memory.io[REG_JOYP] & 0x30) {
|
||||
case 0x30:
|
||||
keys = 0;
|
||||
// TODO: Increment
|
||||
keys = (gb->video.sgbCommandHeader >> 3) == SGB_MLT_REG ? 0xF : 0;
|
||||
break;
|
||||
case 0x20:
|
||||
keys >>= 4;
|
||||
|
@ -553,6 +596,9 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
|||
case REG_SVBK:
|
||||
// Handled transparently by the registers
|
||||
goto success;
|
||||
case REG_DMA:
|
||||
mLOG(GB_IO, STUB, "Reading from unknown register FF%02X", address);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -615,4 +661,10 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_SCX, state->io[REG_SCX]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_WY, state->io[REG_WY]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_WX, state->io[REG_WX]);
|
||||
if (gb->model == GB_MODEL_SGB) {
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_BGP, state->io[REG_BGP]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_OBP0, state->io[REG_OBP0]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_OBP1, state->io[REG_OBP1]);
|
||||
}
|
||||
gb->video.stat = state->io[REG_STAT];
|
||||
}
|
||||
|
|
55
src/gb/mbc.c
55
src/gb/mbc.c
|
@ -68,6 +68,28 @@ void GBMBCSwitchBank0(struct GB* gb, int bank) {
|
|||
}
|
||||
}
|
||||
|
||||
void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_CART_HALFBANK;
|
||||
if (bankStart + GB_SIZE_CART_HALFBANK > gb->memory.romSize) {
|
||||
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
|
||||
bankStart &= (gb->memory.romSize - 1);
|
||||
bank = bankStart / GB_SIZE_CART_HALFBANK;
|
||||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
}
|
||||
if (!half) {
|
||||
gb->memory.romBank = &gb->memory.rom[bankStart];
|
||||
gb->memory.currentBank = bank;
|
||||
} else {
|
||||
gb->memory.mbcState.mbc6.romBank1 = &gb->memory.rom[bankStart];
|
||||
gb->memory.mbcState.mbc6.currentBank1 = bank;
|
||||
}
|
||||
if (gb->cpu->pc < GB_BASE_VRAM) {
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isMulticart(const uint8_t* mem) {
|
||||
bool success = true;
|
||||
struct VFile* vf;
|
||||
|
@ -496,11 +518,34 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
|
||||
void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC6 unimplemented");
|
||||
UNUSED(gb);
|
||||
UNUSED(address);
|
||||
UNUSED(value);
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x7F;
|
||||
switch (address >> 10) {
|
||||
case 0:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x9:
|
||||
GBMBCSwitchHalfBank(gb, 0, bank);
|
||||
break;
|
||||
case 0xD:
|
||||
GBMBCSwitchHalfBank(gb, 1, bank);
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
|
|
|
@ -207,10 +207,14 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
|||
case GB_REGION_CART_BANK0 + 2:
|
||||
case GB_REGION_CART_BANK0 + 3:
|
||||
return memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case GB_REGION_CART_BANK1:
|
||||
case GB_REGION_CART_BANK1 + 1:
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
if (memory->mbcType == GB_MBC6) {
|
||||
return memory->mbcState.mbc6.romBank1[address & (GB_SIZE_CART_HALFBANK - 1)];
|
||||
}
|
||||
// Fall through
|
||||
case GB_REGION_CART_BANK1:
|
||||
case GB_REGION_CART_BANK1 + 1:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case GB_REGION_VRAM:
|
||||
case GB_REGION_VRAM + 1:
|
||||
|
@ -465,10 +469,13 @@ void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
|
|||
bool wasHdma = gb->memory.isHdma;
|
||||
gb->memory.isHdma = value & 0x80;
|
||||
if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) {
|
||||
gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10;
|
||||
if (gb->memory.isHdma) {
|
||||
gb->memory.hdmaRemaining = 0x10;
|
||||
} else {
|
||||
gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10;
|
||||
}
|
||||
gb->cpuBlocked = true;
|
||||
mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0);
|
||||
gb->cpu->nextEvent = gb->cpu->cycles;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -706,8 +713,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
}
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
// TODO?
|
||||
//LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
memory->mbcState.mbc7.state = state->memory.mbc7.state;
|
||||
|
|
|
@ -19,6 +19,7 @@ static const struct GBCartridgeOverride _overrides[] = {
|
|||
bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverride* override) {
|
||||
override->model = GB_MODEL_AUTODETECT;
|
||||
override->mbc = GB_MBC_AUTODETECT;
|
||||
memset(override->gbColors, 0, sizeof(override->gbColors));
|
||||
bool found = false;
|
||||
|
||||
int i;
|
||||
|
@ -35,30 +36,24 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
|
|||
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32);
|
||||
const char* model = ConfigurationGetValue(config, sectionName, "model");
|
||||
const char* mbc = ConfigurationGetValue(config, sectionName, "mbc");
|
||||
const char* pal[4] = {
|
||||
const char* pal[12] = {
|
||||
ConfigurationGetValue(config, sectionName, "pal[0]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[1]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[2]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[3]")
|
||||
ConfigurationGetValue(config, sectionName, "pal[3]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[4]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[5]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[6]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[7]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[8]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[9]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[10]"),
|
||||
ConfigurationGetValue(config, sectionName, "pal[11]")
|
||||
};
|
||||
|
||||
if (model) {
|
||||
if (strcasecmp(model, "DMG") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG;
|
||||
} else if (strcasecmp(model, "CGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_CGB;
|
||||
} else if (strcasecmp(model, "AGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_AGB;
|
||||
} else if (strcasecmp(model, "SGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG; // TODO
|
||||
} else if (strcasecmp(model, "MGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG; // TODO
|
||||
}
|
||||
override->model = GBNameToModel(model);
|
||||
found = override->model != GB_MODEL_AUTODETECT;
|
||||
}
|
||||
|
||||
if (mbc) {
|
||||
|
@ -70,18 +65,25 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
|
|||
}
|
||||
}
|
||||
|
||||
if (pal[0] && pal[1] && pal[2] && pal[3]) {
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
char* end;
|
||||
unsigned long value = strtoul(pal[i], &end, 10);
|
||||
if (end == &pal[i][1] && *end == 'x') {
|
||||
value = strtoul(pal[i], &end, 16);
|
||||
}
|
||||
if (*end) {
|
||||
continue;
|
||||
}
|
||||
override->gbColors[i] = value;
|
||||
for (i = 0; i < 12; ++i) {
|
||||
if (!pal[i]) {
|
||||
continue;
|
||||
}
|
||||
char* end;
|
||||
unsigned long value = strtoul(pal[i], &end, 10);
|
||||
if (end == &pal[i][1] && *end == 'x') {
|
||||
value = strtoul(pal[i], &end, 16);
|
||||
}
|
||||
if (*end) {
|
||||
continue;
|
||||
}
|
||||
value |= 0xFF000000;
|
||||
override->gbColors[i] = value;
|
||||
if (i < 8) {
|
||||
override->gbColors[i + 4] = value;
|
||||
}
|
||||
if (i < 4) {
|
||||
override->gbColors[i + 8] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,31 +93,46 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
|
|||
void GBOverrideSave(struct Configuration* config, const struct GBCartridgeOverride* override) {
|
||||
char sectionName[24] = "";
|
||||
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32);
|
||||
const char* model = 0;
|
||||
switch (override->model) {
|
||||
case GB_MODEL_DMG:
|
||||
model = "DMG";
|
||||
break;
|
||||
case GB_MODEL_SGB:
|
||||
model = "SGB";
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
model = "CGB";
|
||||
break;
|
||||
case GB_MODEL_AGB:
|
||||
model = "AGB";
|
||||
break;
|
||||
case GB_MODEL_AUTODETECT:
|
||||
break;
|
||||
}
|
||||
const char* model = GBModelToName(override->model);
|
||||
ConfigurationSetValue(config, sectionName, "model", model);
|
||||
|
||||
if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[0]", override->gbColors[0]);
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[1]", override->gbColors[1]);
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[2]", override->gbColors[2]);
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[3]", override->gbColors[3]);
|
||||
if (override->gbColors[0] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[0]", override->gbColors[0] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[1] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[1]", override->gbColors[1] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[2] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[2]", override->gbColors[2] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[3] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[3]", override->gbColors[3] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[4] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[4]", override->gbColors[4] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[5] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[5]", override->gbColors[5] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[6] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[6]", override->gbColors[6] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[7] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[7]", override->gbColors[7] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[8] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[8]", override->gbColors[8] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[9] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[9]", override->gbColors[9] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[10] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[10]", override->gbColors[10] & ~0xFF000000);
|
||||
}
|
||||
if (override->gbColors[11] & 0xFF000000) {
|
||||
ConfigurationSetIntValue(config, sectionName, "pal[11]", override->gbColors[11] & ~0xFF000000);
|
||||
}
|
||||
|
||||
if (override->mbc != GB_MBC_AUTODETECT) {
|
||||
ConfigurationSetIntValue(config, sectionName, "mbc", override->mbc);
|
||||
} else {
|
||||
|
@ -133,11 +150,18 @@ void GBOverrideApply(struct GB* gb, const struct GBCartridgeOverride* override)
|
|||
GBMBCInit(gb);
|
||||
}
|
||||
|
||||
if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) {
|
||||
GBVideoSetPalette(&gb->video, 0, override->gbColors[0]);
|
||||
GBVideoSetPalette(&gb->video, 1, override->gbColors[1]);
|
||||
GBVideoSetPalette(&gb->video, 2, override->gbColors[2]);
|
||||
GBVideoSetPalette(&gb->video, 3, override->gbColors[3]);
|
||||
int i;
|
||||
for (i = 0; i < 12; ++i) {
|
||||
if (!(override->gbColors[i] & 0xFF000000)) {
|
||||
continue;
|
||||
}
|
||||
GBVideoSetPalette(&gb->video, i, override->gbColors[i]);
|
||||
if (i < 8) {
|
||||
GBVideoSetPalette(&gb->video, i + 4, override->gbColors[i]);
|
||||
}
|
||||
if (i < 4) {
|
||||
GBVideoSetPalette(&gb->video, i + 8, override->gbColors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* Copyright (c) 2013-2017 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 <mgba/internal/gb/renderers/cache-set.h>
|
||||
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
#include <mgba/internal/gb/video.h>
|
||||
|
||||
void GBVideoCacheInit(struct mCacheSet* cache) {
|
||||
mCacheSetInit(cache, 2, 1);
|
||||
mTileCacheConfiguration config = 0;
|
||||
config = mTileCacheSystemInfoSetPaletteBPP(config, 1); // 2^(2^1) = 4 entries
|
||||
config = mTileCacheSystemInfoSetPaletteCount(config, 4); // 16 palettes
|
||||
config = mTileCacheSystemInfoSetMaxTiles(config, 1024);
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, 0));
|
||||
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 0), config, 0, 0);
|
||||
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 0));
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 1));
|
||||
mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
}
|
||||
|
||||
void GBVideoCacheAssociate(struct mCacheSet* cache, struct GBVideo* video) {
|
||||
mCacheSetAssignVRAM(cache, video->vram);
|
||||
video->renderer->cache = cache;
|
||||
size_t i;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
mCacheSetWritePalette(cache, i, mColorFrom555(video->palette[i]));
|
||||
}
|
||||
mMapCacheSystemInfo sysconfig = mMapCacheSystemInfoSetPaletteCount(0, 0);
|
||||
if (video->p->model >= GB_MODEL_CGB) {
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteCount(0, 2);
|
||||
}
|
||||
mMapCacheConfigureSystem(mMapCacheSetGetPointer(&cache->maps, 0), sysconfig);
|
||||
mMapCacheConfigureSystem(mMapCacheSetGetPointer(&cache->maps, 1), sysconfig);
|
||||
|
||||
GBVideoCacheWriteVideoRegister(cache, REG_LCDC, video->p->memory.io[REG_LCDC]);
|
||||
}
|
||||
|
||||
static void mapParserDMG0(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
int map = *(uint8_t*) vram;
|
||||
entry->tileId = map;
|
||||
entry->flags = mMapCacheEntryFlagsClearHMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsClearVMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, 0);
|
||||
}
|
||||
|
||||
static void mapParserDMG1(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
int map = *(int8_t*) vram;
|
||||
entry->tileId = map + 128;
|
||||
entry->flags = mMapCacheEntryFlagsClearHMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsClearVMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, 0);
|
||||
}
|
||||
|
||||
static void mapParserCGB0(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
int map = *(uint8_t*) vram;
|
||||
uint8_t attr = ((uint8_t*) vram)[0x2000];
|
||||
entry->tileId = map + GBObjAttributesGetBank(attr) * 512;
|
||||
entry->flags = mMapCacheEntryFlagsSetHMirror(entry->flags, GBObjAttributesGetXFlip(attr));
|
||||
entry->flags = mMapCacheEntryFlagsSetVMirror(entry->flags, GBObjAttributesGetYFlip(attr));
|
||||
entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, GBObjAttributesGetCGBPalette(attr));
|
||||
}
|
||||
|
||||
static void mapParserCGB1(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
int map = *(int8_t*) vram;
|
||||
uint8_t attr = ((uint8_t*) vram)[0x2000];
|
||||
entry->tileId = map + 128 + GBObjAttributesGetBank(attr) * 512;
|
||||
entry->flags = mMapCacheEntryFlagsSetHMirror(entry->flags, GBObjAttributesGetXFlip(attr));
|
||||
entry->flags = mMapCacheEntryFlagsSetVMirror(entry->flags, GBObjAttributesGetYFlip(attr));
|
||||
entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, GBObjAttributesGetCGBPalette(attr));
|
||||
}
|
||||
|
||||
void GBVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint16_t address, uint8_t value) {
|
||||
if (address != REG_LCDC) {
|
||||
return;
|
||||
}
|
||||
struct mMapCache* map = mMapCacheSetGetPointer(&cache->maps, 0);
|
||||
struct mMapCache* window = mMapCacheSetGetPointer(&cache->maps, 1);
|
||||
|
||||
mMapCacheSystemInfo sysconfig = mMapCacheSystemInfoIsPaletteCount(map->sysConfig);
|
||||
int tileStart = 0;
|
||||
int mapStart = GB_BASE_MAP;
|
||||
int windowStart = GB_BASE_MAP;
|
||||
if (GBRegisterLCDCIsTileMap(value)) {
|
||||
mapStart += GB_SIZE_MAP;
|
||||
}
|
||||
if (GBRegisterLCDCIsWindowTileMap(value)) {
|
||||
windowStart += GB_SIZE_MAP;
|
||||
}
|
||||
if (GBRegisterLCDCIsTileData(value)) {
|
||||
if (!sysconfig) {
|
||||
map->mapParser = mapParserDMG0;
|
||||
window->mapParser = mapParserDMG0;
|
||||
} else {
|
||||
map->mapParser = mapParserCGB0;
|
||||
window->mapParser = mapParserCGB0;
|
||||
}
|
||||
} else {
|
||||
if (!sysconfig) {
|
||||
map->mapParser = mapParserDMG1;
|
||||
window->mapParser = mapParserDMG1;
|
||||
} else {
|
||||
map->mapParser = mapParserCGB1;
|
||||
window->mapParser = mapParserCGB1;
|
||||
}
|
||||
tileStart = 0x80;
|
||||
}
|
||||
map->tileStart = tileStart;
|
||||
window->tileStart = tileStart;
|
||||
sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 5);
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 1);
|
||||
sysconfig = mMapCacheSystemInfoSetMapAlign(sysconfig, 0);
|
||||
sysconfig = mMapCacheSystemInfoSetTilesHigh(sysconfig, 5);
|
||||
sysconfig = mMapCacheSystemInfoSetTilesWide(sysconfig, 5);
|
||||
mMapCacheConfigureSystem(map, sysconfig);
|
||||
mMapCacheConfigureSystem(window, sysconfig);
|
||||
mMapCacheConfigureMap(map, mapStart);
|
||||
mMapCacheConfigureMap(window, windowStart);
|
||||
}
|
|
@ -5,13 +5,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gb/renderers/software.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
#include <mgba/internal/gb/renderers/cache-set.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
|
||||
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
|
||||
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
|
||||
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
|
||||
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
|
@ -25,9 +28,13 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
|
|||
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
|
||||
|
||||
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
|
||||
size_t sgbOffset = 0;
|
||||
if (renderer->model == GB_MODEL_SGB && renderer->sgbBorders) {
|
||||
sgbOffset = renderer->outputBufferStride * 40 + 48;
|
||||
}
|
||||
int y;
|
||||
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
|
||||
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
|
||||
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y + sgbOffset];
|
||||
int x;
|
||||
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
|
||||
row[x + 0] = renderer->palette[0];
|
||||
|
@ -38,10 +45,112 @@ static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _regenerateSGBBorder(struct GBVideoSoftwareRenderer* renderer) {
|
||||
int i;
|
||||
for (i = 0; i < 0x40; ++i) {
|
||||
uint16_t color;
|
||||
LOAD_16LE(color, 0x800 + i * 2, renderer->d.sgbMapRam);
|
||||
renderer->d.writePalette(&renderer->d, i + 0x40, color);
|
||||
}
|
||||
int x, y;
|
||||
for (y = 0; y < 224; ++y) {
|
||||
for (x = 0; x < 256; x += 8) {
|
||||
if (x >= 48 && x < 208 && y >= 40 && y < 104) {
|
||||
continue;
|
||||
}
|
||||
uint16_t mapData;
|
||||
LOAD_16LE(mapData, (x >> 2) + (y & ~7) * 8, renderer->d.sgbMapRam);
|
||||
if (UNLIKELY(SGBBgAttributesGetTile(mapData) >= 0x100)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int localY = y & 0x7;
|
||||
if (SGBBgAttributesIsYFlip(mapData)) {
|
||||
localY = 7 - localY;
|
||||
}
|
||||
uint8_t tileData[4];
|
||||
tileData[0] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x00];
|
||||
tileData[1] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x01];
|
||||
tileData[2] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x10];
|
||||
tileData[3] = renderer->d.sgbCharRam[(SGBBgAttributesGetTile(mapData) * 16 + localY) * 2 + 0x11];
|
||||
|
||||
size_t base = y * renderer->outputBufferStride + x;
|
||||
int p = SGBBgAttributesGetPalette(mapData) * 0x10;
|
||||
if (SGBBgAttributesIsXFlip(mapData)) {
|
||||
renderer->outputBuffer[base + 0] = renderer->palette[p | ((tileData[0] >> 0) & 0x1) | ((tileData[1] << 1) & 0x2) | ((tileData[2] << 2) & 0x4) | ((tileData[3] << 3) & 0x8)];
|
||||
renderer->outputBuffer[base + 1] = renderer->palette[p | ((tileData[0] >> 1) & 0x1) | ((tileData[1] >> 0) & 0x2) | ((tileData[2] << 1) & 0x4) | ((tileData[3] << 2) & 0x8)];
|
||||
renderer->outputBuffer[base + 2] = renderer->palette[p | ((tileData[0] >> 2) & 0x1) | ((tileData[1] >> 1) & 0x2) | ((tileData[2] >> 0) & 0x4) | ((tileData[3] << 1) & 0x8)];
|
||||
renderer->outputBuffer[base + 3] = renderer->palette[p | ((tileData[0] >> 3) & 0x1) | ((tileData[1] >> 2) & 0x2) | ((tileData[2] >> 1) & 0x4) | ((tileData[3] >> 0) & 0x8)];
|
||||
renderer->outputBuffer[base + 4] = renderer->palette[p | ((tileData[0] >> 4) & 0x1) | ((tileData[1] >> 3) & 0x2) | ((tileData[2] >> 2) & 0x4) | ((tileData[3] >> 1) & 0x8)];
|
||||
renderer->outputBuffer[base + 5] = renderer->palette[p | ((tileData[0] >> 5) & 0x1) | ((tileData[1] >> 4) & 0x2) | ((tileData[2] >> 3) & 0x4) | ((tileData[3] >> 2) & 0x8)];
|
||||
renderer->outputBuffer[base + 6] = renderer->palette[p | ((tileData[0] >> 6) & 0x1) | ((tileData[1] >> 5) & 0x2) | ((tileData[2] >> 4) & 0x4) | ((tileData[3] >> 3) & 0x8)];
|
||||
renderer->outputBuffer[base + 7] = renderer->palette[p | ((tileData[0] >> 7) & 0x1) | ((tileData[1] >> 6) & 0x2) | ((tileData[2] >> 5) & 0x4) | ((tileData[3] >> 4) & 0x8)];
|
||||
} else {
|
||||
renderer->outputBuffer[base + 0] = renderer->palette[p | ((tileData[0] >> 7) & 0x1) | ((tileData[1] >> 6) & 0x2) | ((tileData[2] >> 5) & 0x4) | ((tileData[3] >> 4) & 0x8)];
|
||||
renderer->outputBuffer[base + 1] = renderer->palette[p | ((tileData[0] >> 6) & 0x1) | ((tileData[1] >> 5) & 0x2) | ((tileData[2] >> 4) & 0x4) | ((tileData[3] >> 3) & 0x8)];
|
||||
renderer->outputBuffer[base + 2] = renderer->palette[p | ((tileData[0] >> 5) & 0x1) | ((tileData[1] >> 4) & 0x2) | ((tileData[2] >> 3) & 0x4) | ((tileData[3] >> 2) & 0x8)];
|
||||
renderer->outputBuffer[base + 3] = renderer->palette[p | ((tileData[0] >> 4) & 0x1) | ((tileData[1] >> 3) & 0x2) | ((tileData[2] >> 2) & 0x4) | ((tileData[3] >> 1) & 0x8)];
|
||||
renderer->outputBuffer[base + 4] = renderer->palette[p | ((tileData[0] >> 3) & 0x1) | ((tileData[1] >> 2) & 0x2) | ((tileData[2] >> 1) & 0x4) | ((tileData[3] >> 0) & 0x8)];
|
||||
renderer->outputBuffer[base + 5] = renderer->palette[p | ((tileData[0] >> 2) & 0x1) | ((tileData[1] >> 1) & 0x2) | ((tileData[2] >> 0) & 0x4) | ((tileData[3] << 1) & 0x8)];
|
||||
renderer->outputBuffer[base + 6] = renderer->palette[p | ((tileData[0] >> 1) & 0x1) | ((tileData[1] >> 0) & 0x2) | ((tileData[2] << 1) & 0x4) | ((tileData[3] << 2) & 0x8)];
|
||||
renderer->outputBuffer[base + 7] = renderer->palette[p | ((tileData[0] >> 0) & 0x1) | ((tileData[1] << 1) & 0x2) | ((tileData[2] << 2) & 0x4) | ((tileData[3] << 3) & 0x8)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _setAttribute(uint8_t* sgbAttributes, unsigned x, unsigned y, int palette) {
|
||||
int p = sgbAttributes[(x >> 2) + 5 * y];
|
||||
p &= ~(3 << (2 * (3 - (x & 3))));
|
||||
p |= palette << (2 * (3 - (x & 3)));
|
||||
sgbAttributes[(x >> 2) + 5 * y] = p;
|
||||
}
|
||||
|
||||
static void _parseAttrBlock(struct GBVideoSoftwareRenderer* renderer, int start) {
|
||||
uint8_t block[6];
|
||||
if (start < 0) {
|
||||
memcpy(block, renderer->sgbPartialDataSet, -start);
|
||||
memcpy(&block[-start], renderer->sgbPacket, 6 + start);
|
||||
} else {
|
||||
memcpy(block, &renderer->sgbPacket[start], 6);
|
||||
}
|
||||
unsigned x0 = block[2];
|
||||
unsigned x1 = block[4];
|
||||
unsigned y0 = block[3];
|
||||
unsigned y1 = block[5];
|
||||
unsigned x, y;
|
||||
int pIn = block[1] & 3;
|
||||
int pPerim = (block[1] >> 2) & 3;
|
||||
int pOut = (block[1] >> 4) & 3;
|
||||
|
||||
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS / 8; ++y) {
|
||||
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS / 8; ++x) {
|
||||
if (y > y0 && y < y1 && x > x0 && x < x1) {
|
||||
if (block[0] & 1) {
|
||||
_setAttribute(renderer->d.sgbAttributes, x, y, pIn);
|
||||
}
|
||||
} else if (y < y0 || y > y1 || x < x0 || x > x1) {
|
||||
if (block[0] & 4) {
|
||||
_setAttribute(renderer->d.sgbAttributes, x, y, pOut);
|
||||
}
|
||||
} else {
|
||||
if (block[0] & 2) {
|
||||
_setAttribute(renderer->d.sgbAttributes, x, y, pPerim);
|
||||
} else if (block[0] & 1) {
|
||||
_setAttribute(renderer->d.sgbAttributes, x, y, pIn);
|
||||
} else if (block[0] & 4) {
|
||||
_setAttribute(renderer->d.sgbAttributes, x, y, pOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
|
||||
renderer->d.init = GBVideoSoftwareRendererInit;
|
||||
renderer->d.deinit = GBVideoSoftwareRendererDeinit;
|
||||
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
|
||||
renderer->d.writeSGBPacket = GBVideoSoftwareRendererWriteSGBPacket;
|
||||
renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
|
||||
renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
|
||||
|
@ -58,7 +167,7 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
|
|||
renderer->temporaryBuffer = 0;
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool sgbBorders) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
softwareRenderer->lcdc = 0;
|
||||
softwareRenderer->scy = 0;
|
||||
|
@ -67,6 +176,16 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
|
|||
softwareRenderer->currentWy = 0;
|
||||
softwareRenderer->wx = 0;
|
||||
softwareRenderer->model = model;
|
||||
softwareRenderer->sgbTransfer = 0;
|
||||
softwareRenderer->sgbCommandHeader = 0;
|
||||
softwareRenderer->sgbBorders = sgbBorders;
|
||||
int i;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
softwareRenderer->lookup[i] = i;
|
||||
softwareRenderer->lookup[i] = i;
|
||||
softwareRenderer->lookup[i] = i;
|
||||
softwareRenderer->lookup[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
|
||||
|
@ -76,6 +195,9 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
|
|||
|
||||
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
if (renderer->cache) {
|
||||
GBVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
||||
}
|
||||
switch (address) {
|
||||
case REG_LCDC:
|
||||
softwareRenderer->lcdc = value;
|
||||
|
@ -92,37 +214,87 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
|
|||
case REG_WX:
|
||||
softwareRenderer->wx = value;
|
||||
break;
|
||||
case REG_BGP:
|
||||
softwareRenderer->lookup[0] = value & 3;
|
||||
softwareRenderer->lookup[1] = (value >> 2) & 3;
|
||||
softwareRenderer->lookup[2] = (value >> 4) & 3;
|
||||
softwareRenderer->lookup[3] = (value >> 6) & 3;
|
||||
break;
|
||||
case REG_OBP0:
|
||||
softwareRenderer->lookup[0x20 + 0] = value & 3;
|
||||
softwareRenderer->lookup[0x20 + 1] = (value >> 2) & 3;
|
||||
softwareRenderer->lookup[0x20 + 2] = (value >> 4) & 3;
|
||||
softwareRenderer->lookup[0x20 + 3] = (value >> 6) & 3;
|
||||
break;
|
||||
case REG_OBP1:
|
||||
softwareRenderer->lookup[0x24 + 0] = value & 3;
|
||||
softwareRenderer->lookup[0x24 + 1] = (value >> 2) & 3;
|
||||
softwareRenderer->lookup[0x24 + 2] = (value >> 4) & 3;
|
||||
softwareRenderer->lookup[0x24 + 3] = (value >> 6) & 3;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
memcpy(softwareRenderer->sgbPacket, data, sizeof(softwareRenderer->sgbPacket));
|
||||
int i;
|
||||
if (!(softwareRenderer->sgbCommandHeader & 7)) {
|
||||
softwareRenderer->sgbCommandHeader = data[0];
|
||||
softwareRenderer->sgbPacketId = 0;
|
||||
softwareRenderer->sgbTransfer = 0;
|
||||
}
|
||||
--softwareRenderer->sgbCommandHeader;
|
||||
++softwareRenderer->sgbPacketId;
|
||||
int set;
|
||||
switch (softwareRenderer->sgbCommandHeader >> 3) {
|
||||
case SGB_PAL_SET:
|
||||
softwareRenderer->sgbPacket[1] = data[9];
|
||||
if (!(data[9] & 0x80)) {
|
||||
break;
|
||||
}
|
||||
// Fall through
|
||||
case SGB_ATTR_SET:
|
||||
set = softwareRenderer->sgbPacket[1] & 0x3F;
|
||||
if (set <= 0x2C) {
|
||||
memcpy(renderer->sgbAttributes, &renderer->sgbAttributeFiles[set * 90], 90);
|
||||
}
|
||||
break;
|
||||
case SGB_ATTR_BLK:
|
||||
if (softwareRenderer->sgbPacketId == 1) {
|
||||
softwareRenderer->sgbDataSets = softwareRenderer->sgbPacket[1];
|
||||
i = 2;
|
||||
} else {
|
||||
i = (9 - softwareRenderer->sgbPacketId) % 3 * -2;
|
||||
}
|
||||
for (; i <= 10 && softwareRenderer->sgbDataSets; i += 6, --softwareRenderer->sgbDataSets) {
|
||||
_parseAttrBlock(softwareRenderer, i);
|
||||
}
|
||||
if (i < 16 && softwareRenderer->sgbDataSets) {
|
||||
memcpy(softwareRenderer->sgbPartialDataSet, &softwareRenderer->sgbPacket[i], 16 - i);
|
||||
}
|
||||
break;
|
||||
case SGB_ATRC_EN:
|
||||
if (softwareRenderer->sgbBorders) {
|
||||
_regenerateSGBBorder(softwareRenderer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
color_t color = 0;
|
||||
color |= (value & 0x001F) << 11;
|
||||
color |= (value & 0x03E0) << 1;
|
||||
color |= (value & 0x7C00) >> 10;
|
||||
#else
|
||||
color_t color = value;
|
||||
#endif
|
||||
#else
|
||||
color_t color = 0;
|
||||
color |= (value << 3) & 0xF8;
|
||||
color |= (value << 6) & 0xF800;
|
||||
color |= (value << 9) & 0xF80000;
|
||||
color |= (color >> 5) & 0x070707;
|
||||
#endif
|
||||
color_t color = mColorFrom555(value);
|
||||
softwareRenderer->palette[index] = color;
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, index << 1);
|
||||
mCacheSetWritePalette(renderer->cache, index, color);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,28 +339,149 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
|
|||
GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
|
||||
}
|
||||
}
|
||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||
int x;
|
||||
for (x = startX; x + 7 < (endX & ~7); x += 8) {
|
||||
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
|
||||
row[x + 1] = softwareRenderer->palette[softwareRenderer->row[x + 1] & 0x7F];
|
||||
row[x + 2] = softwareRenderer->palette[softwareRenderer->row[x + 2] & 0x7F];
|
||||
row[x + 3] = softwareRenderer->palette[softwareRenderer->row[x + 3] & 0x7F];
|
||||
row[x + 4] = softwareRenderer->palette[softwareRenderer->row[x + 4] & 0x7F];
|
||||
row[x + 5] = softwareRenderer->palette[softwareRenderer->row[x + 5] & 0x7F];
|
||||
row[x + 6] = softwareRenderer->palette[softwareRenderer->row[x + 6] & 0x7F];
|
||||
row[x + 7] = softwareRenderer->palette[softwareRenderer->row[x + 7] & 0x7F];
|
||||
|
||||
size_t sgbOffset = 0;
|
||||
if (softwareRenderer->model == GB_MODEL_SGB && softwareRenderer->sgbBorders) {
|
||||
sgbOffset = softwareRenderer->outputBufferStride * 40 + 48;
|
||||
}
|
||||
for (; x < endX; ++x) {
|
||||
row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
|
||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + sgbOffset];
|
||||
int x = startX;
|
||||
int p = 0;
|
||||
switch (softwareRenderer->d.sgbRenderMode) {
|
||||
case 0:
|
||||
if (softwareRenderer->model == GB_MODEL_SGB) {
|
||||
p = softwareRenderer->d.sgbAttributes[(startX >> 5) + 5 * (y >> 3)];
|
||||
p >>= 6 - ((x / 4) & 0x6);
|
||||
p &= 3;
|
||||
p <<= 2;
|
||||
}
|
||||
for (; x < ((startX + 7) & ~7) && x < endX; ++x) {
|
||||
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
|
||||
}
|
||||
for (; x + 7 < (endX & ~7); x += 8) {
|
||||
if (softwareRenderer->model == GB_MODEL_SGB) {
|
||||
p = softwareRenderer->d.sgbAttributes[(x >> 5) + 5 * (y >> 3)];
|
||||
p >>= 6 - ((x / 4) & 0x6);
|
||||
p &= 3;
|
||||
p <<= 2;
|
||||
}
|
||||
row[x + 0] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
|
||||
row[x + 1] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 1] & 0x7F]];
|
||||
row[x + 2] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 2] & 0x7F]];
|
||||
row[x + 3] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 3] & 0x7F]];
|
||||
row[x + 4] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 4] & 0x7F]];
|
||||
row[x + 5] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 5] & 0x7F]];
|
||||
row[x + 6] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 6] & 0x7F]];
|
||||
row[x + 7] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x + 7] & 0x7F]];
|
||||
}
|
||||
if (softwareRenderer->model == GB_MODEL_SGB) {
|
||||
p = softwareRenderer->d.sgbAttributes[(x >> 5) + 5 * (y >> 3)];
|
||||
p >>= 6 - ((x / 4) & 0x6);
|
||||
p &= 3;
|
||||
p <<= 2;
|
||||
}
|
||||
for (; x < endX; ++x) {
|
||||
row[x] = softwareRenderer->palette[p | softwareRenderer->lookup[softwareRenderer->row[x] & 0x7F]];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
for (; x < ((startX + 7) & ~7) && x < endX; ++x) {
|
||||
row[x] = 0;
|
||||
}
|
||||
for (; x + 7 < (endX & ~7); x += 8) {
|
||||
row[x] = 0;
|
||||
row[x + 1] = 0;
|
||||
row[x + 2] = 0;
|
||||
row[x + 3] = 0;
|
||||
row[x + 4] = 0;
|
||||
row[x + 5] = 0;
|
||||
row[x + 6] = 0;
|
||||
row[x + 7] = 0;
|
||||
}
|
||||
for (; x < endX; ++x) {
|
||||
row[x] = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (; x < ((startX + 7) & ~7) && x < endX; ++x) {
|
||||
row[x] = softwareRenderer->palette[0];
|
||||
}
|
||||
for (; x + 7 < (endX & ~7); x += 8) {
|
||||
row[x] = softwareRenderer->palette[0];
|
||||
row[x + 1] = softwareRenderer->palette[0];
|
||||
row[x + 2] = softwareRenderer->palette[0];
|
||||
row[x + 3] = softwareRenderer->palette[0];
|
||||
row[x + 4] = softwareRenderer->palette[0];
|
||||
row[x + 5] = softwareRenderer->palette[0];
|
||||
row[x + 6] = softwareRenderer->palette[0];
|
||||
row[x + 7] = softwareRenderer->palette[0];
|
||||
}
|
||||
for (; x < endX; ++x) {
|
||||
row[x] = softwareRenderer->palette[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
|
||||
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) {
|
||||
++softwareRenderer->currentWy;
|
||||
}
|
||||
if (softwareRenderer->sgbTransfer == 1) {
|
||||
size_t offset = 2 * ((y & 7) + (y >> 3) * GB_VIDEO_HORIZONTAL_PIXELS);
|
||||
if (offset >= 0x1000) {
|
||||
return;
|
||||
}
|
||||
uint8_t* buffer = NULL;
|
||||
switch (softwareRenderer->sgbCommandHeader >> 3) {
|
||||
case SGB_PAL_TRN:
|
||||
buffer = renderer->sgbPalRam;
|
||||
break;
|
||||
case SGB_CHR_TRN:
|
||||
buffer = &renderer->sgbCharRam[SGB_SIZE_CHAR_RAM / 2 * (softwareRenderer->sgbPacket[1] & 1)];
|
||||
break;
|
||||
case SGB_PCT_TRN:
|
||||
buffer = renderer->sgbMapRam;
|
||||
break;
|
||||
case SGB_ATTR_TRN:
|
||||
buffer = renderer->sgbAttributeFiles;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (buffer) {
|
||||
int i;
|
||||
for (i = 0; i < GB_VIDEO_HORIZONTAL_PIXELS; i += 8) {
|
||||
if (UNLIKELY(offset + (i << 1) + 1 >= 0x1000)) {
|
||||
break;
|
||||
}
|
||||
uint8_t hi = 0;
|
||||
uint8_t lo = 0;
|
||||
hi |= (softwareRenderer->row[i + 0] & 0x2) << 6;
|
||||
lo |= (softwareRenderer->row[i + 0] & 0x1) << 7;
|
||||
hi |= (softwareRenderer->row[i + 1] & 0x2) << 5;
|
||||
lo |= (softwareRenderer->row[i + 1] & 0x1) << 6;
|
||||
hi |= (softwareRenderer->row[i + 2] & 0x2) << 4;
|
||||
lo |= (softwareRenderer->row[i + 2] & 0x1) << 5;
|
||||
hi |= (softwareRenderer->row[i + 3] & 0x2) << 3;
|
||||
lo |= (softwareRenderer->row[i + 3] & 0x1) << 4;
|
||||
hi |= (softwareRenderer->row[i + 4] & 0x2) << 2;
|
||||
lo |= (softwareRenderer->row[i + 4] & 0x1) << 3;
|
||||
hi |= (softwareRenderer->row[i + 5] & 0x2) << 1;
|
||||
lo |= (softwareRenderer->row[i + 5] & 0x1) << 2;
|
||||
hi |= (softwareRenderer->row[i + 6] & 0x2) << 0;
|
||||
lo |= (softwareRenderer->row[i + 6] & 0x1) << 1;
|
||||
hi |= (softwareRenderer->row[i + 7] & 0x2) >> 1;
|
||||
lo |= (softwareRenderer->row[i + 7] & 0x1) >> 0;
|
||||
buffer[offset + (i << 1) + 0] = lo;
|
||||
buffer[offset + (i << 1) + 1] = hi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
|
||||
|
@ -201,6 +494,32 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
|
|||
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
|
||||
_clearScreen(softwareRenderer);
|
||||
}
|
||||
if (softwareRenderer->model == GB_MODEL_SGB) {
|
||||
switch (softwareRenderer->sgbCommandHeader >> 3) {
|
||||
case SGB_PAL_SET:
|
||||
case SGB_ATTR_SET:
|
||||
if (softwareRenderer->sgbPacket[1] & 0x40) {
|
||||
renderer->sgbRenderMode = 0;
|
||||
}
|
||||
break;
|
||||
case SGB_PAL_TRN:
|
||||
case SGB_CHR_TRN:
|
||||
case SGB_PCT_TRN:
|
||||
if (softwareRenderer->sgbTransfer > 0 && softwareRenderer->sgbBorders) {
|
||||
// Make sure every buffer sees this if we're multibuffering
|
||||
_regenerateSGBBorder(softwareRenderer);
|
||||
}
|
||||
// Fall through
|
||||
case SGB_ATTR_TRN:
|
||||
++softwareRenderer->sgbTransfer;
|
||||
if (softwareRenderer->sgbTransfer == 5) {
|
||||
softwareRenderer->sgbCommandHeader = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
softwareRenderer->currentWy = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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 <mgba/internal/gb/renderers/tile-cache.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gb/video.h>
|
||||
#include <mgba/internal/gb/renderers/tile-cache.h>
|
||||
|
||||
void GBVideoTileCacheInit(struct mTileCache* cache) {
|
||||
mTileCacheInit(cache);
|
||||
mTileCacheConfiguration config = 0;
|
||||
config = mTileCacheSystemInfoSetPalette0BPP(config, 1); // 2^(2^2) = 4 entries
|
||||
config = mTileCacheSystemInfoSetPalette0Count(config, 4); // 16 palettes
|
||||
config = mTileCacheSystemInfoSetPalette1BPP(config, 0); // Disable
|
||||
config = mTileCacheSystemInfoSetPalette1Count(config, 0); // Disable
|
||||
config = mTileCacheSystemInfoSetMaxTiles(config, 1024);
|
||||
mTileCacheConfigureSystem(cache, config);
|
||||
}
|
||||
|
||||
void GBVideoTileCacheAssociate(struct mTileCache* cache, struct GBVideo* video) {
|
||||
cache->vram = (uint16_t*) video->vram;
|
||||
cache->palette = video->palette;
|
||||
video->renderer->cache = cache;
|
||||
}
|
|
@ -12,7 +12,10 @@
|
|||
mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize");
|
||||
|
||||
const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
|
||||
const uint32_t GB_SAVESTATE_VERSION = 0x00000001;
|
||||
const uint32_t GB_SAVESTATE_VERSION = 0x00000002;
|
||||
|
||||
static void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state);
|
||||
static void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state);
|
||||
|
||||
void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
||||
STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);
|
||||
|
@ -20,7 +23,7 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
|||
STORE_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
|
||||
|
||||
if (gb->memory.rom) {
|
||||
memcpy(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title));
|
||||
memcpy(state->title, ((struct GBCartridge*) &gb->memory.rom[0x100])->titleLong, sizeof(state->title));
|
||||
} else {
|
||||
memset(state->title, 0, sizeof(state->title));
|
||||
}
|
||||
|
@ -44,7 +47,6 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
|||
STORE_16LE(gb->cpu->index, 0, &state->cpu.index);
|
||||
state->cpu.bus = gb->cpu->bus;
|
||||
state->cpu.executionState = gb->cpu->executionState;
|
||||
STORE_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
|
||||
|
||||
GBSerializedCpuFlags flags = 0;
|
||||
flags = GBSerializedCpuFlagsSetCondition(flags, gb->cpu->condition);
|
||||
|
@ -59,6 +61,10 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
|||
GBVideoSerialize(&gb->video, state);
|
||||
GBTimerSerialize(&gb->timer, state);
|
||||
GBAudioSerialize(&gb->audio, state);
|
||||
|
||||
if (gb->model == GB_MODEL_SGB) {
|
||||
GBSGBSerialize(gb, state);
|
||||
}
|
||||
}
|
||||
|
||||
bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||
|
@ -77,10 +83,15 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
} else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) {
|
||||
mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
|
||||
}
|
||||
bool canSgb = ucheck >= GB_SAVESTATE_MAGIC + 2;
|
||||
|
||||
if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) {
|
||||
mLOG(GB_STATE, WARN, "Savestate is for a different game");
|
||||
error = true;
|
||||
if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) &gb->memory.rom[0x100])->titleLong, sizeof(state->title))) {
|
||||
LOAD_32LE(ucheck, 0, &state->versionMagic);
|
||||
if (ucheck > GB_SAVESTATE_MAGIC + 2 || memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) {
|
||||
// There was a bug in previous versions where the memory address being compared was wrong
|
||||
mLOG(GB_STATE, WARN, "Savestate is for a different game");
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
LOAD_32LE(ucheck, 0, &state->romCrc32);
|
||||
if (ucheck != gb->romCrc32) {
|
||||
|
@ -126,6 +137,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
return false;
|
||||
}
|
||||
gb->timing.root = NULL;
|
||||
LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
|
||||
|
||||
gb->cpu->a = state->cpu.a;
|
||||
gb->cpu->f.packed = state->cpu.f;
|
||||
|
@ -141,7 +153,6 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
LOAD_16LE(gb->cpu->index, 0, &state->cpu.index);
|
||||
gb->cpu->bus = state->cpu.bus;
|
||||
gb->cpu->executionState = state->cpu.executionState;
|
||||
LOAD_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
|
||||
|
||||
GBSerializedCpuFlags flags;
|
||||
LOAD_32LE(flags, 0, &state->cpu.flags);
|
||||
|
@ -174,7 +185,74 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
GBTimerDeserialize(&gb->timer, state);
|
||||
GBAudioDeserialize(&gb->audio, state);
|
||||
|
||||
if (gb->model == GB_MODEL_SGB && canSgb) {
|
||||
GBSGBDeserialize(gb, state);
|
||||
}
|
||||
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
|
||||
gb->timing.reroot = gb->timing.root;
|
||||
gb->timing.root = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Reorganize SGB into its own file
|
||||
void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
||||
state->sgb.command = gb->video.sgbCommandHeader;
|
||||
state->sgb.bits = gb->sgbBit;
|
||||
|
||||
GBSerializedSGBFlags flags = 0;
|
||||
flags = GBSerializedSGBFlagsSetP1Bits(flags, gb->currentSgbBits);
|
||||
flags = GBSerializedSGBFlagsSetRenderMode(flags, gb->video.renderer->sgbRenderMode);
|
||||
STORE_32LE(flags, 0, &state->sgb.flags);
|
||||
|
||||
memcpy(state->sgb.packet, gb->sgbPacket, sizeof(state->sgb.packet));
|
||||
|
||||
if (gb->video.renderer->sgbCharRam) {
|
||||
memcpy(state->sgb.charRam, gb->video.renderer->sgbCharRam, sizeof(state->sgb.charRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbMapRam) {
|
||||
memcpy(state->sgb.mapRam, gb->video.renderer->sgbMapRam, sizeof(state->sgb.mapRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbPalRam) {
|
||||
memcpy(state->sgb.palRam, gb->video.renderer->sgbPalRam, sizeof(state->sgb.palRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbAttributeFiles) {
|
||||
memcpy(state->sgb.atfRam, gb->video.renderer->sgbAttributeFiles, sizeof(state->sgb.atfRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbAttributes) {
|
||||
memcpy(state->sgb.attributes, gb->video.renderer->sgbAttributes, sizeof(state->sgb.attributes));
|
||||
}
|
||||
}
|
||||
|
||||
void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||
gb->video.sgbCommandHeader = state->sgb.command;
|
||||
gb->sgbBit = state->sgb.bits;
|
||||
|
||||
GBSerializedSGBFlags flags;
|
||||
LOAD_32LE(flags, 0, &state->sgb.flags);
|
||||
gb->currentSgbBits = GBSerializedSGBFlagsGetP1Bits(flags);
|
||||
gb->video.renderer->sgbRenderMode = GBSerializedSGBFlagsGetRenderMode(flags);
|
||||
|
||||
memcpy(gb->sgbPacket, state->sgb.packet, sizeof(state->sgb.packet));
|
||||
|
||||
if (gb->video.renderer->sgbCharRam) {
|
||||
memcpy(gb->video.renderer->sgbCharRam, state->sgb.charRam, sizeof(state->sgb.charRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbMapRam) {
|
||||
memcpy(gb->video.renderer->sgbMapRam, state->sgb.mapRam, sizeof(state->sgb.mapRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbPalRam) {
|
||||
memcpy(gb->video.renderer->sgbPalRam, state->sgb.palRam, sizeof(state->sgb.palRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbAttributeFiles) {
|
||||
memcpy(gb->video.renderer->sgbAttributeFiles, state->sgb.atfRam, sizeof(state->sgb.atfRam));
|
||||
}
|
||||
if (gb->video.renderer->sgbAttributes) {
|
||||
memcpy(gb->video.renderer->sgbAttributes, state->sgb.attributes, sizeof(state->sgb.attributes));
|
||||
}
|
||||
|
||||
GBVideoWriteSGBPacket(&gb->video, (uint8_t[16]) { (SGB_ATRC_EN << 3) | 1, 0 });
|
||||
GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ M_TEST_DEFINE(reset) {
|
|||
struct mCore* core = GBCoreCreate();
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
core->deinit(core);
|
||||
}
|
||||
|
@ -37,6 +38,7 @@ M_TEST_DEFINE(loadNullROM) {
|
|||
struct mCore* core = GBCoreCreate();
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
assert_false(core->loadROM(core, NULL));
|
||||
core->reset(core);
|
||||
core->deinit(core);
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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/test/suite.h"
|
||||
|
||||
M_TEST_SUITE_DECLARE(GBCore);
|
||||
M_TEST_SUITE_DECLARE(GBMBC);
|
||||
M_TEST_SUITE_DECLARE(GBMemory);
|
||||
M_TEST_SUITE_DECLARE(GBRTC);
|
||||
|
||||
int TestRunGB(void) {
|
||||
int failures = 0;
|
||||
failures += M_TEST_SUITE_RUN(GBCore);
|
||||
failures += M_TEST_SUITE_RUN(GBMBC);
|
||||
failures += M_TEST_SUITE_RUN(GBMemory);
|
||||
failures += M_TEST_SUITE_RUN(GBRTC);
|
||||
return failures;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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 TEST_GB_H
|
||||
#define TEST_GB_H
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
int TestRunGB(void);
|
||||
|
||||
#endif
|
|
@ -16,6 +16,7 @@ M_TEST_SUITE_SETUP(GBMBC) {
|
|||
GBSynthesizeROM(vf);
|
||||
struct mCore* core = GBCoreCreate();
|
||||
core->init(core);
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->loadROM(core, vf);
|
||||
*state = core;
|
||||
return 0;
|
||||
|
|
|
@ -16,6 +16,7 @@ M_TEST_SUITE_SETUP(GBMemory) {
|
|||
GBSynthesizeROM(vf);
|
||||
struct mCore* core = GBCoreCreate();
|
||||
core->init(core);
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->loadROM(core, vf);
|
||||
*state = core;
|
||||
return 0;
|
||||
|
|
|
@ -48,6 +48,7 @@ M_TEST_SUITE_SETUP(GBRTC) {
|
|||
return -1;
|
||||
}
|
||||
test->core->init(test->core);
|
||||
mCoreInitConfig(test->core, NULL);
|
||||
struct VFile* vf = VFileMemChunk(NULL, 2048);
|
||||
GBSynthesizeROM(vf);
|
||||
test->core->loadROM(test->core, vf);
|
||||
|
|
|
@ -64,7 +64,6 @@ void GBTimerReset(struct GBTimer* timer) {
|
|||
|
||||
timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
|
||||
timer->timaPeriod = 1024 >> 4;
|
||||
timer->internalDiv = 0;
|
||||
}
|
||||
|
||||
void GBTimerDivReset(struct GBTimer* timer) {
|
||||
|
|
285
src/gb/video.c
285
src/gb/video.c
|
@ -7,17 +7,19 @@
|
|||
|
||||
#include <mgba/core/sync.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
#include <mgba/internal/gb/renderers/cache-set.h>
|
||||
#include <mgba/internal/gb/serialize.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
|
||||
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
|
||||
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
|
||||
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
|
@ -39,6 +41,7 @@ static struct GBVideoRenderer dummyRenderer = {
|
|||
.init = GBVideoDummyRendererInit,
|
||||
.deinit = GBVideoDummyRendererDeinit,
|
||||
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
|
||||
.writeSGBPacket = GBVideoDummyRendererWriteSGBPacket,
|
||||
.writeVRAM = GBVideoDummyRendererWriteVRAM,
|
||||
.writeOAM = GBVideoDummyRendererWriteOAM,
|
||||
.writePalette = GBVideoDummyRendererWritePalette,
|
||||
|
@ -52,6 +55,7 @@ static struct GBVideoRenderer dummyRenderer = {
|
|||
void GBVideoInit(struct GBVideo* video) {
|
||||
video->renderer = &dummyRenderer;
|
||||
video->renderer->cache = NULL;
|
||||
video->renderer->sgbRenderMode = 0;
|
||||
video->vram = 0;
|
||||
video->frameskip = 0;
|
||||
|
||||
|
@ -68,6 +72,22 @@ void GBVideoInit(struct GBVideo* video) {
|
|||
video->dmgPalette[1] = 0x56B5;
|
||||
video->dmgPalette[2] = 0x294A;
|
||||
video->dmgPalette[3] = 0x0000;
|
||||
video->dmgPalette[4] = 0x7FFF;
|
||||
video->dmgPalette[5] = 0x56B5;
|
||||
video->dmgPalette[6] = 0x294A;
|
||||
video->dmgPalette[7] = 0x0000;
|
||||
video->dmgPalette[8] = 0x7FFF;
|
||||
video->dmgPalette[9] = 0x56B5;
|
||||
video->dmgPalette[10] = 0x294A;
|
||||
video->dmgPalette[11] = 0x0000;
|
||||
|
||||
video->sgbBorders = true;
|
||||
|
||||
video->renderer->sgbCharRam = NULL;
|
||||
video->renderer->sgbMapRam = NULL;
|
||||
video->renderer->sgbPalRam = NULL;
|
||||
video->renderer->sgbAttributes = NULL;
|
||||
video->renderer->sgbAttributeFiles = NULL;
|
||||
}
|
||||
|
||||
void GBVideoReset(struct GBVideo* video) {
|
||||
|
@ -89,21 +109,83 @@ void GBVideoReset(struct GBVideo* video) {
|
|||
video->renderer->oam = &video->oam;
|
||||
memset(&video->palette, 0, sizeof(video->palette));
|
||||
|
||||
if (video->p->model == GB_MODEL_SGB) {
|
||||
video->renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM);
|
||||
video->renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM);
|
||||
video->renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM);
|
||||
video->renderer->sgbAttributeFiles = anonymousMemoryMap(SGB_SIZE_ATF_RAM);
|
||||
video->renderer->sgbAttributes = malloc(90 * 45);
|
||||
memset(video->renderer->sgbAttributes, 0, 90 * 45);
|
||||
video->sgbCommandHeader = 0;
|
||||
}
|
||||
|
||||
video->palette[0] = video->dmgPalette[0];
|
||||
video->palette[1] = video->dmgPalette[1];
|
||||
video->palette[2] = video->dmgPalette[2];
|
||||
video->palette[3] = video->dmgPalette[3];
|
||||
video->palette[8 * 4 + 0] = video->dmgPalette[4];
|
||||
video->palette[8 * 4 + 1] = video->dmgPalette[5];
|
||||
video->palette[8 * 4 + 2] = video->dmgPalette[6];
|
||||
video->palette[8 * 4 + 3] = video->dmgPalette[7];
|
||||
video->palette[9 * 4 + 0] = video->dmgPalette[8];
|
||||
video->palette[9 * 4 + 1] = video->dmgPalette[9];
|
||||
video->palette[9 * 4 + 2] = video->dmgPalette[10];
|
||||
video->palette[9 * 4 + 3] = video->dmgPalette[11];
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer, video->p->model);
|
||||
video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
|
||||
|
||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
||||
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
||||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 3, video->palette[9 * 4 + 3]);
|
||||
}
|
||||
|
||||
void GBVideoDeinit(struct GBVideo* video) {
|
||||
GBVideoAssociateRenderer(video, &dummyRenderer);
|
||||
mappedMemoryFree(video->vram, GB_SIZE_VRAM);
|
||||
if (video->renderer->sgbCharRam) {
|
||||
mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
|
||||
video->renderer->sgbCharRam = NULL;
|
||||
}
|
||||
if (video->renderer->sgbMapRam) {
|
||||
mappedMemoryFree(video->renderer->sgbMapRam, SGB_SIZE_MAP_RAM);
|
||||
video->renderer->sgbMapRam = NULL;
|
||||
}
|
||||
if (video->renderer->sgbPalRam) {
|
||||
mappedMemoryFree(video->renderer->sgbPalRam, SGB_SIZE_PAL_RAM);
|
||||
video->renderer->sgbPalRam = NULL;
|
||||
}
|
||||
if (video->renderer->sgbAttributeFiles) {
|
||||
mappedMemoryFree(video->renderer->sgbAttributeFiles, SGB_SIZE_ATF_RAM);
|
||||
video->renderer->sgbAttributeFiles = NULL;
|
||||
}
|
||||
if (video->renderer->sgbAttributes) {
|
||||
free(video->renderer->sgbAttributes);
|
||||
video->renderer->sgbAttributes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {
|
||||
video->renderer->deinit(video->renderer);
|
||||
renderer->cache = video->renderer->cache;
|
||||
renderer->sgbRenderMode = video->renderer->sgbRenderMode;
|
||||
renderer->sgbCharRam = video->renderer->sgbCharRam;
|
||||
renderer->sgbMapRam = video->renderer->sgbMapRam;
|
||||
renderer->sgbPalRam = video->renderer->sgbPalRam;
|
||||
renderer->sgbAttributeFiles = video->renderer->sgbAttributeFiles;
|
||||
renderer->sgbAttributes = video->renderer->sgbAttributes;
|
||||
video->renderer = renderer;
|
||||
renderer->vram = video->vram;
|
||||
video->renderer->init(video->renderer, video->p->model);
|
||||
video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
|
||||
}
|
||||
|
||||
static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) {
|
||||
|
@ -154,6 +236,7 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
video->mode = 1;
|
||||
video->modeEvent.callback = _endMode1;
|
||||
|
||||
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
|
||||
|
||||
if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||
|
@ -230,6 +313,7 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
GBVideoProcessDots(video);
|
||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
||||
video->p->memory.hdmaRemaining = 0x10;
|
||||
video->p->cpuBlocked = true;
|
||||
mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
|
||||
mTimingSchedule(timing, &video->p->memory.hdmaEvent, 0);
|
||||
}
|
||||
|
@ -263,10 +347,10 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
}
|
||||
|
||||
GBFrameEnded(video->p);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
--video->frameskipCounter;
|
||||
if (video->frameskipCounter < 0) {
|
||||
video->renderer->finishFrame(video->renderer);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
video->frameskipCounter = video->frameskip;
|
||||
}
|
||||
++video->frameCounter;
|
||||
|
@ -342,9 +426,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
|
||||
video->ly = 0;
|
||||
video->p->memory.io[REG_LY] = 0;
|
||||
// TODO: Does this read as 0 for 4 T-cycles?
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, 0);
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
|
@ -372,7 +455,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = (video->stat & 0x7) | (value & 0x78);
|
||||
if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
|
||||
if (video->p->model < GB_MODEL_CGB && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
|
||||
// TODO: variable for the IRQ line value?
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
|
@ -390,7 +473,7 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {
|
||||
if (video->p->model < GB_MODEL_CGB) {
|
||||
if (video->p->model < GB_MODEL_SGB) {
|
||||
switch (address) {
|
||||
case REG_BGP:
|
||||
video->palette[0] = video->dmgPalette[value & 3];
|
||||
|
@ -403,26 +486,28 @@ void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value)
|
|||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||
break;
|
||||
case REG_OBP0:
|
||||
video->palette[8 * 4 + 0] = video->dmgPalette[value & 3];
|
||||
video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||
video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||
video->palette[8 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
|
||||
video->palette[8 * 4 + 0] = video->dmgPalette[(value & 3) + 4];
|
||||
video->palette[8 * 4 + 1] = video->dmgPalette[((value >> 2) & 3) + 4];
|
||||
video->palette[8 * 4 + 2] = video->dmgPalette[((value >> 4) & 3) + 4];
|
||||
video->palette[8 * 4 + 3] = video->dmgPalette[((value >> 6) & 3) + 4];
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
|
||||
break;
|
||||
case REG_OBP1:
|
||||
video->palette[9 * 4 + 0] = video->dmgPalette[value & 3];
|
||||
video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||
video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||
video->palette[9 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
|
||||
video->palette[9 * 4 + 0] = video->dmgPalette[(value & 3) + 8];
|
||||
video->palette[9 * 4 + 1] = video->dmgPalette[((value >> 2) & 3) + 8];
|
||||
video->palette[9 * 4 + 2] = video->dmgPalette[((value >> 4) & 3) + 8];
|
||||
video->palette[9 * 4 + 3] = video->dmgPalette[((value >> 6) & 3) + 8];
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 3, video->palette[9 * 4 + 3]);
|
||||
break;
|
||||
}
|
||||
} else if (video->p->model == GB_MODEL_SGB) {
|
||||
video->renderer->writeVideoRegister(video->renderer, address, value);
|
||||
} else {
|
||||
switch (address) {
|
||||
case REG_BCPD:
|
||||
|
@ -470,15 +555,160 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color) {
|
||||
if (index >= 4) {
|
||||
if (index >= 12) {
|
||||
return;
|
||||
}
|
||||
video->dmgPalette[index] = M_RGB8_TO_RGB5(color);
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||
void GBVideoDisableCGB(struct GBVideo* video) {
|
||||
video->dmgPalette[0] = video->palette[0];
|
||||
video->dmgPalette[1] = video->palette[1];
|
||||
video->dmgPalette[2] = video->palette[2];
|
||||
video->dmgPalette[3] = video->palette[3];
|
||||
video->dmgPalette[4] = video->palette[8 * 4 + 0];
|
||||
video->dmgPalette[5] = video->palette[8 * 4 + 1];
|
||||
video->dmgPalette[6] = video->palette[8 * 4 + 2];
|
||||
video->dmgPalette[7] = video->palette[8 * 4 + 3];
|
||||
video->dmgPalette[8] = video->palette[9 * 4 + 0];
|
||||
video->dmgPalette[9] = video->palette[9 * 4 + 1];
|
||||
video->dmgPalette[10] = video->palette[9 * 4 + 2];
|
||||
video->dmgPalette[11] = video->palette[9 * 4 + 3];
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
|
||||
}
|
||||
|
||||
void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
|
||||
int i;
|
||||
if (!(video->sgbCommandHeader & 7)) {
|
||||
if ((data[0] >> 3) > SGB_OBJ_TRN) {
|
||||
video->sgbCommandHeader = 0;
|
||||
return;
|
||||
}
|
||||
video->sgbCommandHeader = data[0];
|
||||
}
|
||||
--video->sgbCommandHeader;
|
||||
switch (video->sgbCommandHeader >> 3) {
|
||||
case SGB_PAL01:
|
||||
video->palette[0] = data[1] | (data[2] << 8);
|
||||
video->palette[1] = data[3] | (data[4] << 8);
|
||||
video->palette[2] = data[5] | (data[6] << 8);
|
||||
video->palette[3] = data[7] | (data[8] << 8);
|
||||
|
||||
video->palette[16] = data[1] | (data[2] << 8);
|
||||
video->palette[17] = data[9] | (data[10] << 8);
|
||||
video->palette[18] = data[11] | (data[12] << 8);
|
||||
video->palette[19] = data[13] | (data[14] << 8);
|
||||
|
||||
video->palette[32] = data[1] | (data[2] << 8);
|
||||
video->palette[48] = data[1] | (data[2] << 8);
|
||||
|
||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
||||
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
||||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||
video->renderer->writePalette(video->renderer, 16, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 17, video->palette[17]);
|
||||
video->renderer->writePalette(video->renderer, 18, video->palette[18]);
|
||||
video->renderer->writePalette(video->renderer, 19, video->palette[19]);
|
||||
video->renderer->writePalette(video->renderer, 32, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 48, video->palette[0]);
|
||||
break;
|
||||
case SGB_PAL23:
|
||||
video->palette[33] = data[3] | (data[4] << 8);
|
||||
video->palette[34] = data[5] | (data[6] << 8);
|
||||
video->palette[35] = data[7] | (data[8] << 8);
|
||||
|
||||
video->palette[49] = data[9] | (data[10] << 8);
|
||||
video->palette[50] = data[11] | (data[12] << 8);
|
||||
video->palette[51] = data[13] | (data[14] << 8);
|
||||
video->renderer->writePalette(video->renderer, 33, video->palette[33]);
|
||||
video->renderer->writePalette(video->renderer, 34, video->palette[34]);
|
||||
video->renderer->writePalette(video->renderer, 35, video->palette[35]);
|
||||
video->renderer->writePalette(video->renderer, 49, video->palette[49]);
|
||||
video->renderer->writePalette(video->renderer, 50, video->palette[50]);
|
||||
video->renderer->writePalette(video->renderer, 51, video->palette[51]);
|
||||
break;
|
||||
case SGB_PAL03:
|
||||
video->palette[0] = data[1] | (data[2] << 8);
|
||||
video->palette[1] = data[3] | (data[4] << 8);
|
||||
video->palette[2] = data[5] | (data[6] << 8);
|
||||
video->palette[3] = data[7] | (data[8] << 8);
|
||||
|
||||
video->palette[16] = data[1] | (data[2] << 8);
|
||||
video->palette[32] = data[1] | (data[2] << 8);
|
||||
|
||||
video->palette[48] = data[1] | (data[2] << 8);
|
||||
video->palette[49] = data[9] | (data[10] << 8);
|
||||
video->palette[50] = data[11] | (data[12] << 8);
|
||||
video->palette[51] = data[13] | (data[14] << 8);
|
||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
||||
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
||||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||
video->renderer->writePalette(video->renderer, 16, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 32, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 48, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 49, video->palette[49]);
|
||||
video->renderer->writePalette(video->renderer, 50, video->palette[50]);
|
||||
video->renderer->writePalette(video->renderer, 51, video->palette[51]);
|
||||
break;
|
||||
case SGB_PAL12:
|
||||
video->palette[17] = data[3] | (data[4] << 8);
|
||||
video->palette[18] = data[5] | (data[6] << 8);
|
||||
video->palette[19] = data[7] | (data[8] << 8);
|
||||
|
||||
video->palette[33] = data[9] | (data[10] << 8);
|
||||
video->palette[34] = data[11] | (data[12] << 8);
|
||||
video->palette[35] = data[13] | (data[14] << 8);
|
||||
video->renderer->writePalette(video->renderer, 17, video->palette[17]);
|
||||
video->renderer->writePalette(video->renderer, 18, video->palette[18]);
|
||||
video->renderer->writePalette(video->renderer, 19, video->palette[19]);
|
||||
video->renderer->writePalette(video->renderer, 33, video->palette[33]);
|
||||
video->renderer->writePalette(video->renderer, 34, video->palette[34]);
|
||||
video->renderer->writePalette(video->renderer, 35, video->palette[35]);
|
||||
break;
|
||||
case SGB_PAL_SET:
|
||||
for (i = 0; i < 4; ++i) {
|
||||
uint16_t entry = (data[2 + (i * 2)] << 8) | data[1 + (i * 2)];
|
||||
if (entry >= 0x200) {
|
||||
mLOG(GB, STUB, "Unimplemented SGB palette overflow: %03X", entry);
|
||||
continue;
|
||||
}
|
||||
LOAD_16LE(video->palette[i * 4 + 0], entry * 8 + 0, video->renderer->sgbPalRam);
|
||||
video->renderer->writePalette(video->renderer, i * 4 + 0, video->palette[0]);
|
||||
LOAD_16LE(video->palette[i * 4 + 1], entry * 8 + 2, video->renderer->sgbPalRam);
|
||||
video->renderer->writePalette(video->renderer, i * 4 + 1, video->palette[i * 4 + 1]);
|
||||
LOAD_16LE(video->palette[i * 4 + 2], entry * 8 + 4, video->renderer->sgbPalRam);
|
||||
video->renderer->writePalette(video->renderer, i * 4 + 2, video->palette[i * 4 + 2]);
|
||||
LOAD_16LE(video->palette[i * 4 + 3], entry * 8 + 6, video->renderer->sgbPalRam);
|
||||
video->renderer->writePalette(video->renderer, i * 4 + 3, video->palette[i * 4 + 3]);
|
||||
}
|
||||
break;
|
||||
case SGB_ATTR_BLK:
|
||||
case SGB_PAL_TRN:
|
||||
case SGB_ATRC_EN:
|
||||
case SGB_CHR_TRN:
|
||||
case SGB_PCT_TRN:
|
||||
case SGB_ATTR_TRN:
|
||||
case SGB_ATTR_SET:
|
||||
break;
|
||||
case SGB_MLT_REG:
|
||||
return;
|
||||
case SGB_MASK_EN:
|
||||
video->renderer->sgbRenderMode = data[1] & 0x3;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB, STUB, "Unimplemented SGB command: %02X", data[0] >> 3);
|
||||
return;
|
||||
}
|
||||
video->renderer->writeSGBPacket(video->renderer, data);
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(model);
|
||||
UNUSED(borders);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
|
@ -488,14 +718,20 @@ static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer) {
|
|||
}
|
||||
|
||||
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(address);
|
||||
if (renderer->cache) {
|
||||
GBVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(data);
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,9 +742,8 @@ static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint1
|
|||
}
|
||||
|
||||
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
|
||||
UNUSED(value);
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, index << 1);
|
||||
mCacheSetWritePalette(renderer->cache, index, mColorFrom555(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -628,5 +863,5 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
|
|||
GBVideoSwitchBank(video, video->vramCurrentBank);
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer, video->p->model);
|
||||
video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ static void _RegisterRamReset(struct GBA* gba) {
|
|||
memset(gba->video.palette, 0, SIZE_PALETTE_RAM);
|
||||
}
|
||||
if (registers & 0x08) {
|
||||
memset(gba->video.renderer->vram, 0, SIZE_VRAM);
|
||||
memset(gba->video.vram, 0, SIZE_VRAM);
|
||||
}
|
||||
if (registers & 0x10) {
|
||||
memset(gba->video.oam.raw, 0, SIZE_OAM);
|
||||
|
|
|
@ -139,7 +139,8 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_
|
|||
char line[18] = "XXXXXXXX XXXXXXXX";
|
||||
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
|
||||
|
||||
int gsaP, parP;
|
||||
int gsaP, rgsaP, parP, rparP;
|
||||
int maxProbability = INT_MIN;
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
// Try to detect GameShark version
|
||||
|
@ -147,27 +148,42 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_
|
|||
gsaP = GBACheatGameSharkProbability(o1, o2);
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
if (gsaP > maxProbability) {
|
||||
maxProbability = gsaP;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1);
|
||||
}
|
||||
|
||||
GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
|
||||
parP = GBACheatProActionReplayProbability(o1, o2);
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
if (gsaP > parP) {
|
||||
GBACheatSetGameSharkVersion(set, 1);
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
if (parP > maxProbability) {
|
||||
maxProbability = parP;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3);
|
||||
}
|
||||
|
||||
rgsaP = GBACheatGameSharkProbability(op1, op1);
|
||||
if (rgsaP > maxProbability) {
|
||||
maxProbability = rgsaP;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW);
|
||||
}
|
||||
|
||||
rparP = GBACheatProActionReplayProbability(op1, op1);
|
||||
if (rparP > maxProbability) {
|
||||
maxProbability = rparP;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW);
|
||||
}
|
||||
|
||||
if (set->gsaVersion < 3) {
|
||||
return GBACheatAddGameShark(set, op1, op2);
|
||||
} else {
|
||||
// If probabilities are equal, assume PARv3
|
||||
GBACheatSetGameSharkVersion(set, 3);
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
||||
return GBACheatAddProActionReplay(set, op1, op2);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
case 2:
|
||||
return GBACheatAddGameShark(set, o1, o2);
|
||||
case 3:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
||||
case 4:
|
||||
return GBACheatAddProActionReplay(set, o1, o2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -288,11 +304,19 @@ static void GBACheatParseDirectives(struct mCheatSet* set, const struct StringLi
|
|||
for (d = 0; d < StringListSize(directives); ++d) {
|
||||
const char* directive = *StringListGetConstPointer(directives, d);
|
||||
if (strcmp(directive, "GSAv1") == 0) {
|
||||
GBACheatSetGameSharkVersion(cheats, 1);
|
||||
GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(directive, "GSAv1 raw") == 0) {
|
||||
GBACheatSetGameSharkVersion(cheats, GBA_GS_GSAV1_RAW);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(directive, "PARv3") == 0) {
|
||||
GBACheatSetGameSharkVersion(cheats, 3);
|
||||
GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(directive, "PARv3 raw") == 0) {
|
||||
GBACheatSetGameSharkVersion(cheats, GBA_GS_PARV3_RAW);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -311,15 +335,21 @@ static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* dir
|
|||
char** directive;
|
||||
switch (cheats->gsaVersion) {
|
||||
case 1:
|
||||
case 2:
|
||||
directive = StringListAppend(directives);
|
||||
*directive = strdup("GSAv1");
|
||||
break;
|
||||
case 2:
|
||||
directive = StringListAppend(directives);
|
||||
*directive = strdup("GSAv1 raw");
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
directive = StringListAppend(directives);
|
||||
*directive = strdup("PARv3");
|
||||
break;
|
||||
case 4:
|
||||
directive = StringListAppend(directives);
|
||||
*directive = strdup("PARv3 raw");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,7 +397,7 @@ int GBACheatAddressIsReal(uint32_t address) {
|
|||
return -0x8;
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
if ((address & OFFSET_MASK) > SIZE_CART_SRAM) {
|
||||
if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gba/cheats.h>
|
||||
|
||||
#include "gba/cheats/gameshark.h"
|
||||
#include "gba/cheats/parv3.h"
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
@ -73,17 +74,19 @@ void GBACheatReseedGameShark(uint32_t* seeds, uint16_t params, const uint8_t* t1
|
|||
}
|
||||
}
|
||||
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, int version) {
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, enum GBACheatGameSharkVersion version) {
|
||||
cheats->gsaVersion = version;
|
||||
switch (version) {
|
||||
case 1:
|
||||
case 2:
|
||||
case GBA_GS_GSAV1:
|
||||
case GBA_GS_GSAV1_RAW:
|
||||
memcpy(cheats->gsaSeeds, GBACheatGameSharkSeeds, 4 * sizeof(uint32_t));
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case GBA_GS_PARV3:
|
||||
case GBA_GS_PARV3_RAW:
|
||||
memcpy(cheats->gsaSeeds, GBACheatProActionReplaySeeds, 4 * sizeof(uint32_t));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,14 +200,13 @@ bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
|
|||
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
|
||||
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 4:
|
||||
GBACheatSetGameSharkVersion(set, 1);
|
||||
default:
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1);
|
||||
// Fall through
|
||||
case 1:
|
||||
case 2:
|
||||
case GBA_GS_GSAV1:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
// Fall through
|
||||
case GBA_GS_GSAV1_RAW:
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -12,10 +12,18 @@ CXX_GUARD_START
|
|||
|
||||
extern const uint32_t GBACheatGameSharkSeeds[4];
|
||||
|
||||
enum GBACheatGameSharkVersion {
|
||||
GBA_GS_NOT_SET = 0,
|
||||
GBA_GS_GSAV1 = 1,
|
||||
GBA_GS_GSAV1_RAW = 2,
|
||||
GBA_GS_PARV3 = 3,
|
||||
GBA_GS_PARV3_RAW = 4
|
||||
};
|
||||
|
||||
struct GBACheatSet;
|
||||
void GBACheatDecryptGameShark(uint32_t* op1, uint32_t* op2, const uint32_t* seeds);
|
||||
void GBACheatReseedGameShark(uint32_t* seeds, uint16_t params, const uint8_t* t1, const uint8_t* t2);
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, int version);
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, enum GBACheatGameSharkVersion version);
|
||||
bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2);
|
||||
int GBACheatGameSharkProbability(uint32_t op1, uint32_t op2);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ static uint32_t _parAddr(uint32_t x) {
|
|||
}
|
||||
|
||||
static void _parEndBlock(struct GBACheatSet* cheats) {
|
||||
size_t size = mCheatListSize(&cheats->d.list) - cheats->currentBlock;
|
||||
size_t size = mCheatListSize(&cheats->d.list) - cheats->currentBlock - 1;
|
||||
struct mCheat* currentBlock = mCheatListGetPointer(&cheats->d.list, cheats->currentBlock);
|
||||
if (currentBlock->repeat) {
|
||||
currentBlock->negativeRepeat = size - currentBlock->repeat;
|
||||
|
@ -64,7 +64,7 @@ static void _parEndBlock(struct GBACheatSet* cheats) {
|
|||
}
|
||||
|
||||
static void _parElseBlock(struct GBACheatSet* cheats) {
|
||||
size_t size = mCheatListSize(&cheats->d.list) - cheats->currentBlock;
|
||||
size_t size = mCheatListSize(&cheats->d.list) - cheats->currentBlock - 1;
|
||||
struct mCheat* currentBlock = mCheatListGetPointer(&cheats->d.list, cheats->currentBlock);
|
||||
currentBlock->repeat = size;
|
||||
}
|
||||
|
@ -284,6 +284,10 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin
|
|||
cheat->address = BASE_IO | (op1 & OFFSET_MASK);
|
||||
break;
|
||||
}
|
||||
if (op1 & 0x01000000 && (op1 & 0xFE000000) != 0xC6000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
cheat->width = width;
|
||||
cheat->operand = op2 & (0xFFFFFFFFU >> ((4 - width) * 8));
|
||||
|
@ -297,14 +301,13 @@ bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t
|
|||
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
|
||||
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
GBACheatSetGameSharkVersion(set, 3);
|
||||
default:
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3);
|
||||
// Fall through
|
||||
case 3:
|
||||
case 4:
|
||||
case GBA_GS_PARV3:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
// Fall through
|
||||
case GBA_GS_PARV3_RAW:
|
||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
||||
}
|
||||
return false;
|
||||
|
@ -370,7 +373,7 @@ int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2) {
|
|||
int width = ((op1 & PAR3_WIDTH) >> (PAR3_WIDTH_BASE - 3));
|
||||
if (op1 & PAR3_COND) {
|
||||
probability += 0x20;
|
||||
if (width == 32) {
|
||||
if (width >= 24) {
|
||||
return 0;
|
||||
}
|
||||
if (op2 & ~((1 << width) - 1)) {
|
||||
|
@ -384,10 +387,13 @@ int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2) {
|
|||
if (op2 & ~((1 << width) - 1)) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
// Fall through
|
||||
case PAR3_BASE_ASSIGN:
|
||||
case PAR3_BASE_INDIRECT:
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
// Fall through
|
||||
if (op1 & 0x01000000) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case PAR3_BASE_OTHER:
|
||||
break;
|
||||
|
|
|
@ -266,6 +266,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
|
|||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->renderer.outputBuffer = buffer;
|
||||
gbacore->renderer.outputBufferStride = stride;
|
||||
memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty));
|
||||
}
|
||||
|
||||
static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
|
||||
|
@ -402,14 +403,7 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
GBAVideoAssociateRenderer(&gba->video, renderer);
|
||||
}
|
||||
|
||||
struct GBACartridgeOverride override;
|
||||
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
|
||||
if (cart) {
|
||||
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||
if (GBAOverrideFind(gbacore->overrides, &override)) {
|
||||
GBAOverrideApply(gba, &override);
|
||||
}
|
||||
}
|
||||
GBAOverrideApplyDefaults(gba, gbacore->overrides);
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
if (!gba->biosVf && core->opts.useBios) {
|
||||
|
@ -505,7 +499,6 @@ static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
|
|||
static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->keys &= ~keys;
|
||||
GBATestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static int32_t _GBACoreFrameCounter(const struct mCore* core) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gba/renderers/proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
|
@ -206,7 +206,7 @@ void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t
|
|||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32
|
|||
proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -755,19 +755,17 @@ void GBATestKeypadIRQ(struct GBA* gba) {
|
|||
return;
|
||||
}
|
||||
int isAnd = keycnt & 0x8000;
|
||||
uint16_t keyInput;
|
||||
|
||||
if (!gba->keySource) {
|
||||
// TODO?
|
||||
return;
|
||||
}
|
||||
|
||||
keycnt &= 0x3FF;
|
||||
keyInput = *gba->keySource;
|
||||
uint16_t keyInput = *gba->keySource & keycnt;
|
||||
|
||||
if (isAnd && keycnt == keyInput) {
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD);
|
||||
} else if (!isAnd && keycnt & keyInput) {
|
||||
} else if (!isAnd && keyInput) {
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD);
|
||||
}
|
||||
}
|
||||
|
|
19
src/gba/io.c
19
src/gba/io.c
|
@ -890,14 +890,17 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
break;
|
||||
case REG_MAX:
|
||||
// Some bad interrupt libraries will read from this
|
||||
break;
|
||||
case 0x66:
|
||||
case 0x6E:
|
||||
case 0x76:
|
||||
case 0x7A:
|
||||
case 0x7E:
|
||||
case 0x86:
|
||||
case 0x8A:
|
||||
case 0x066:
|
||||
case 0x06E:
|
||||
case 0x076:
|
||||
case 0x07A:
|
||||
case 0x07E:
|
||||
case 0x086:
|
||||
case 0x08A:
|
||||
case 0x136:
|
||||
case 0x142:
|
||||
case 0x15A:
|
||||
case 0x206:
|
||||
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
|
||||
return 0;
|
||||
case REG_DEBUG_ENABLE:
|
||||
|
|
|
@ -92,6 +92,7 @@ void GBAMemoryDeinit(struct GBA* gba) {
|
|||
if (gba->memory.rom) {
|
||||
mappedMemoryFree(gba->memory.rom, gba->memory.romSize);
|
||||
}
|
||||
gba->memory.savedata.maskWriteback = false;
|
||||
GBASavedataUnmask(&gba->memory.savedata);
|
||||
GBASavedataDeinit(&gba->memory.savedata);
|
||||
if (gba->memory.savedata.realVf) {
|
||||
|
@ -683,27 +684,39 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value);
|
||||
|
||||
#define STORE_PALETTE_RAM \
|
||||
STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \
|
||||
gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \
|
||||
wait += waitstatesRegion[REGION_PALETTE_RAM]; \
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value);
|
||||
LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \
|
||||
if (oldValue != value) { \
|
||||
STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \
|
||||
gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); \
|
||||
} \
|
||||
wait += waitstatesRegion[REGION_PALETTE_RAM];
|
||||
|
||||
#define STORE_VRAM \
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \
|
||||
if (oldValue != value) { \
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||
} \
|
||||
} else { \
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \
|
||||
LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \
|
||||
if (oldValue != value) { \
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \
|
||||
} \
|
||||
} \
|
||||
wait += waitstatesRegion[REGION_VRAM];
|
||||
|
||||
#define STORE_OAM \
|
||||
STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1);
|
||||
LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \
|
||||
if (oldValue != value) { \
|
||||
STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); \
|
||||
}
|
||||
|
||||
#define STORE_CART \
|
||||
wait += waitstatesRegion[address >> BASE_OFFSET]; \
|
||||
|
@ -726,6 +739,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle
|
|||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
int32_t oldValue;
|
||||
char* waitstatesRegion = memory->waitstatesNonseq32;
|
||||
|
||||
switch (address >> BASE_OFFSET) {
|
||||
|
@ -777,6 +791,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
int16_t oldValue;
|
||||
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_WORKING_RAM:
|
||||
|
@ -790,21 +805,33 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
GBAIOWrite(gba, address & (OFFSET_MASK - 1), value);
|
||||
break;
|
||||
case REGION_PALETTE_RAM:
|
||||
STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value);
|
||||
LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
|
||||
if (oldValue != value) {
|
||||
STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
|
||||
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value);
|
||||
}
|
||||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
|
||||
if (value != oldValue) {
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
}
|
||||
} else {
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
|
||||
LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram);
|
||||
if (value != oldValue) {
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1);
|
||||
LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||
if (value != oldValue) {
|
||||
STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1);
|
||||
}
|
||||
break;
|
||||
case REGION_CART0:
|
||||
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) {
|
||||
|
@ -844,6 +871,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
int wait = 0;
|
||||
uint16_t oldValue;
|
||||
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_WORKING_RAM:
|
||||
|
@ -865,8 +893,11 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);
|
||||
break;
|
||||
}
|
||||
gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
oldValue = gba->video.renderer->vram[(address & 0x1FFFE) >> 1];
|
||||
if (oldValue != (((uint8_t) value) | (value << 8))) {
|
||||
gba->video.renderer->vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);
|
||||
|
@ -1369,6 +1400,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum
|
|||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
uint32_t value;
|
||||
uint32_t oldValue;
|
||||
char* waitstatesRegion = memory->waitstatesSeq32;
|
||||
|
||||
int i;
|
||||
|
|
|
@ -338,11 +338,19 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri
|
|||
}
|
||||
}
|
||||
|
||||
void GBAOverrideApplyDefaults(struct GBA* gba) {
|
||||
struct GBACartridgeOverride override;
|
||||
void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overrides) {
|
||||
struct GBACartridgeOverride override = { .idleLoop = IDLE_LOOP_NONE };
|
||||
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
|
||||
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||
if (GBAOverrideFind(0, &override)) {
|
||||
GBAOverrideApply(gba, &override);
|
||||
if (cart) {
|
||||
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||
|
||||
if (!strncmp("pokemon red version", &((const char*) gba->memory.rom)[0x108], 20) && gba->romCrc32 != 0xDD88761C) {
|
||||
// Enable FLASH1M and RTC on Pokémon FireRed ROM hacks
|
||||
override.savetype = SAVEDATA_FLASH1M;
|
||||
override.hardware = HW_RTC;
|
||||
GBAOverrideApply(gba, &override);
|
||||
} else if (GBAOverrideFind(overrides, &override)) {
|
||||
GBAOverrideApply(gba, &override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/* Copyright (c) 2013-2017 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 <mgba/internal/gba/renderers/cache-set.h>
|
||||
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
void GBAVideoCacheInit(struct mCacheSet* cache) {
|
||||
mCacheSetInit(cache, 4, 4);
|
||||
mTileCacheSystemInfo sysconfig = 0;
|
||||
mTileCacheConfiguration config = mTileCacheConfigurationFillShouldStore(0);
|
||||
sysconfig = mTileCacheSystemInfoSetPaletteBPP(sysconfig, 2); // 2^(2^2) = 16 entries
|
||||
sysconfig = mTileCacheSystemInfoSetPaletteCount(sysconfig, 4); // 16 palettes
|
||||
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 2048);
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, 0));
|
||||
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 0), config);
|
||||
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 0), sysconfig, 0, 0);
|
||||
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 1024);
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, 2));
|
||||
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 2), config);
|
||||
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 2), sysconfig, 0x10000, 0x100);
|
||||
|
||||
sysconfig = mTileCacheSystemInfoSetPaletteBPP(sysconfig, 3); // 2^(2^3) = 256 entries
|
||||
sysconfig = mTileCacheSystemInfoSetPaletteCount(sysconfig, 0); // 1 palettes
|
||||
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 2048);
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, 1));
|
||||
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 1), config);
|
||||
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 1), sysconfig, 0, 0);
|
||||
sysconfig = mTileCacheSystemInfoSetMaxTiles(sysconfig, 1024);
|
||||
mTileCacheInit(mTileCacheSetGetPointer(&cache->tiles, 3));
|
||||
mTileCacheConfigure(mTileCacheSetGetPointer(&cache->tiles, 3), config);
|
||||
mTileCacheConfigureSystem(mTileCacheSetGetPointer(&cache->tiles, 3), sysconfig, 0x10000, 0x100);
|
||||
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 0));
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 1));
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 2));
|
||||
mMapCacheInit(mMapCacheSetGetPointer(&cache->maps, 3));
|
||||
}
|
||||
|
||||
void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video) {
|
||||
mCacheSetAssignVRAM(cache, video->vram);
|
||||
video->renderer->cache = cache;
|
||||
size_t i;
|
||||
for (i = 0; i < SIZE_PALETTE_RAM / 2; ++i) {
|
||||
mCacheSetWritePalette(cache, i, mColorFrom555(video->palette[i]));
|
||||
}
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]);
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_BG0CNT, video->p->memory.io[REG_BG0CNT >> 1]);
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_BG1CNT, video->p->memory.io[REG_BG1CNT >> 1]);
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_BG2CNT, video->p->memory.io[REG_BG2CNT >> 1]);
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_BG3CNT, video->p->memory.io[REG_BG3CNT >> 1]);
|
||||
}
|
||||
|
||||
static void mapParser0(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
uint16_t map = *(uint16_t*) vram;
|
||||
entry->tileId = GBA_TEXT_MAP_TILE(map);
|
||||
entry->flags = mMapCacheEntryFlagsSetHMirror(entry->flags, !!GBA_TEXT_MAP_HFLIP(map));
|
||||
entry->flags = mMapCacheEntryFlagsSetVMirror(entry->flags, !!GBA_TEXT_MAP_VFLIP(map));
|
||||
entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, GBA_TEXT_MAP_PALETTE(map));
|
||||
}
|
||||
|
||||
static void mapParser2(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) {
|
||||
UNUSED(cache);
|
||||
entry->tileId = *(uint8_t*) vram;
|
||||
entry->flags = mMapCacheEntryFlagsClearHMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsClearVMirror(entry->flags);
|
||||
entry->flags = mMapCacheEntryFlagsClearPaletteId(entry->flags);
|
||||
}
|
||||
|
||||
static void GBAVideoCacheWriteDISPCNT(struct mCacheSet* cache, uint16_t value) {
|
||||
switch (GBARegisterDISPCNTGetMode(value)) {
|
||||
case 0:
|
||||
default:
|
||||
mMapCacheSetGetPointer(&cache->maps, 0)->mapParser = mapParser0;
|
||||
mMapCacheSetGetPointer(&cache->maps, 1)->mapParser = mapParser0;
|
||||
mMapCacheSetGetPointer(&cache->maps, 2)->mapParser = mapParser0;
|
||||
mMapCacheSetGetPointer(&cache->maps, 3)->mapParser = mapParser0;
|
||||
|
||||
mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 2)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 3)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
mMapCacheSetGetPointer(&cache->maps, 0)->mapParser = mapParser0;
|
||||
mMapCacheSetGetPointer(&cache->maps, 1)->mapParser = mapParser0;
|
||||
mMapCacheSetGetPointer(&cache->maps, 2)->mapParser = mapParser2;
|
||||
mMapCacheSetGetPointer(&cache->maps, 3)->mapParser = mapParser2;
|
||||
|
||||
mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0);
|
||||
mMapCacheSetGetPointer(&cache->maps, 2)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 1);
|
||||
mMapCacheSetGetPointer(&cache->maps, 3)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBAVideoCacheWriteBGCNT(struct mCacheSet* cache, size_t bg, uint16_t value) {
|
||||
struct mMapCache* map = mMapCacheSetGetPointer(&cache->maps, bg);
|
||||
map->context = (void*) (uintptr_t) value;
|
||||
|
||||
int tileStart = GBARegisterBGCNTGetCharBase(value) * 256;
|
||||
bool p = GBARegisterBGCNTGet256Color(value);
|
||||
int size = GBARegisterBGCNTGetSize(value);
|
||||
int tilesWide = 0;
|
||||
int tilesHigh = 0;
|
||||
mMapCacheSystemInfo sysconfig = 0;
|
||||
if (map->mapParser == mapParser0) {
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 2 + p);
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteCount(sysconfig, 4 * !p);
|
||||
sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 5);
|
||||
sysconfig = mMapCacheSystemInfoSetMapAlign(sysconfig, 1);
|
||||
tilesWide = 5;
|
||||
tilesHigh = 5;
|
||||
if (size & 1) {
|
||||
++tilesWide;
|
||||
}
|
||||
if (size & 2) {
|
||||
++tilesHigh;
|
||||
}
|
||||
map->tileStart = tileStart * 2;
|
||||
} else if (map->mapParser == mapParser2) {
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 3);
|
||||
sysconfig = mMapCacheSystemInfoSetPaletteCount(sysconfig, 0);
|
||||
sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 4 + size);
|
||||
sysconfig = mMapCacheSystemInfoSetMapAlign(sysconfig, 0);
|
||||
|
||||
tilesHigh = 4 + size;
|
||||
tilesWide = 4 + size;
|
||||
map->tileStart = tileStart;
|
||||
}
|
||||
sysconfig = mMapCacheSystemInfoSetTilesHigh(sysconfig, tilesHigh);
|
||||
sysconfig = mMapCacheSystemInfoSetTilesWide(sysconfig, tilesWide);
|
||||
mMapCacheConfigureSystem(map, sysconfig);
|
||||
mMapCacheConfigureMap(map, GBARegisterBGCNTGetScreenBase(value) << 11);
|
||||
}
|
||||
|
||||
void GBAVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint32_t address, uint16_t value) {
|
||||
switch (address) {
|
||||
case REG_DISPCNT:
|
||||
GBAVideoCacheWriteDISPCNT(cache, value);
|
||||
GBAVideoCacheWriteBGCNT(cache, 0, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 0)->context);
|
||||
GBAVideoCacheWriteBGCNT(cache, 1, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 1)->context);
|
||||
GBAVideoCacheWriteBGCNT(cache, 2, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 2)->context);
|
||||
GBAVideoCacheWriteBGCNT(cache, 3, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 3)->context);
|
||||
break;
|
||||
case REG_BG0CNT:
|
||||
GBAVideoCacheWriteBGCNT(cache, 0, value);
|
||||
break;
|
||||
case REG_BG1CNT:
|
||||
GBAVideoCacheWriteBGCNT(cache, 1, value);
|
||||
break;
|
||||
case REG_BG2CNT:
|
||||
GBAVideoCacheWriteBGCNT(cache, 2, value);
|
||||
break;
|
||||
case REG_BG3CNT:
|
||||
GBAVideoCacheWriteBGCNT(cache, 3, value);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gba/renderers/software-private.h"
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
||||
#define MODE_2_COORD_OVERFLOW \
|
||||
|
@ -101,21 +102,7 @@ void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer
|
|||
|
||||
if (!mosaicWait) {
|
||||
LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
|
||||
#ifndef COLOR_16_BIT
|
||||
unsigned color32;
|
||||
color32 = 0;
|
||||
color32 |= (color << 3) & 0xF8;
|
||||
color32 |= (color << 6) & 0xF800;
|
||||
color32 |= (color << 9) & 0xF80000;
|
||||
color32 |= (color32 >> 5) & 0x070707;
|
||||
color = color32;
|
||||
#elif COLOR_5_6_5
|
||||
uint16_t color16 = 0;
|
||||
color16 |= (color & 0x001F) << 11;
|
||||
color16 |= (color & 0x03E0) << 1;
|
||||
color16 |= (color & 0x7C00) >> 10;
|
||||
color = color16;
|
||||
#endif
|
||||
color = mColorFrom555(color);
|
||||
mosaicWait = mosaicH;
|
||||
} else {
|
||||
--mosaicWait;
|
||||
|
@ -192,20 +179,7 @@ void GBAVideoSoftwareRendererDrawBackgroundMode5(struct GBAVideoSoftwareRenderer
|
|||
|
||||
if (!mosaicWait) {
|
||||
LOAD_16(color, offset + (localX >> 8) * 2 + (localY >> 8) * 320, renderer->d.vram);
|
||||
#ifndef COLOR_16_BIT
|
||||
unsigned color32 = 0;
|
||||
color32 |= (color << 9) & 0xF80000;
|
||||
color32 |= (color << 3) & 0xF8;
|
||||
color32 |= (color << 6) & 0xF800;
|
||||
color32 |= (color32 >> 5) & 0x070707;
|
||||
color = color32;
|
||||
#elif COLOR_5_6_5
|
||||
uint16_t color16 = 0;
|
||||
color16 |= (color & 0x001F) << 11;
|
||||
color16 |= (color & 0x03E0) << 1;
|
||||
color16 |= (color & 0x7C00) >> 10;
|
||||
color = color16;
|
||||
#endif
|
||||
color = mColorFrom555(color);
|
||||
mosaicWait = mosaicH;
|
||||
} else {
|
||||
--mosaicWait;
|
||||
|
|
|
@ -166,9 +166,13 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
target2 |= renderer->bg[2].target2 << (renderer->bg[2].priority);
|
||||
target2 |= renderer->bg[3].target2 << (renderer->bg[3].priority);
|
||||
if ((1 << GBAObjAttributesCGetPriority(sprite->c)) <= target2) {
|
||||
flags |= FLAG_REBLEND;
|
||||
variant = 0;
|
||||
} else if (!target2) {
|
||||
flags &= ~FLAG_TARGET_1;
|
||||
}
|
||||
}
|
||||
|
||||
color_t* palette = &renderer->normalPalette[0x100];
|
||||
color_t* objwinPalette = palette;
|
||||
int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed);
|
||||
|
|
|
@ -43,7 +43,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
|
|||
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
|
||||
color = _mix(renderer->blda, current, renderer->bldb, color);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
|
||||
color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND);
|
||||
}
|
||||
} else {
|
||||
color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
|
||||
|
@ -59,7 +59,7 @@ static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* rend
|
|||
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
|
||||
color = _mix(renderer->blda, current, renderer->bldb, color);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
|
||||
color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND);
|
||||
}
|
||||
} else {
|
||||
color = color & ~FLAG_TARGET_2;
|
||||
|
@ -73,7 +73,7 @@ static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* rend
|
|||
if (color < current) {
|
||||
color |= (current & FLAG_OBJWIN);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
|
||||
color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND);
|
||||
}
|
||||
*pixel = color;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
uint32_t current) {
|
||||
UNUSED(renderer);
|
||||
if (color >= current) {
|
||||
color = (current & 0x00FFFFFF) | ((current << 1) & FLAG_REBLEND);
|
||||
color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND);
|
||||
}
|
||||
*pixel = color;
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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 <mgba/internal/gba/renderers/tile-cache.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
void GBAVideoTileCacheInit(struct mTileCache* cache) {
|
||||
mTileCacheInit(cache);
|
||||
mTileCacheConfiguration config = 0;
|
||||
config = mTileCacheSystemInfoSetPalette0BPP(config, 2); // 2^(2^2) = 16 entries
|
||||
config = mTileCacheSystemInfoSetPalette0Count(config, 5); // 32 palettes
|
||||
config = mTileCacheSystemInfoSetPalette1BPP(config, 3); // 2^(2^3) = 256 entries
|
||||
config = mTileCacheSystemInfoSetPalette1Count(config, 1); // 2 palettes
|
||||
config = mTileCacheSystemInfoSetMaxTiles(config, 3072);
|
||||
mTileCacheConfigureSystem(cache, config);
|
||||
}
|
||||
|
||||
void GBAVideoTileCacheAssociate(struct mTileCache* cache, struct GBAVideo* video) {
|
||||
cache->vram = video->vram;
|
||||
cache->palette = video->palette;
|
||||
video->renderer->cache = cache;
|
||||
}
|
|
@ -5,13 +5,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gba/renderers/software-private.h"
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/arm/macros.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/renderers/cache-set.h>
|
||||
|
||||
#include <mgba-util/arm-algo.h>
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
#define DIRTY_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] |= (1 << (Y & 0x1F))
|
||||
#define CLEAN_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] &= ~(1 << (Y & 0x1F))
|
||||
|
||||
static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer);
|
||||
|
@ -26,10 +30,6 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer,
|
|||
|
||||
static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
|
||||
static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
|
||||
|
@ -111,6 +111,11 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
|
|||
softwareRenderer->oamMax = 0;
|
||||
|
||||
softwareRenderer->mosaic = 0;
|
||||
softwareRenderer->nextY = 0;
|
||||
|
||||
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
|
||||
memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache));
|
||||
memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo));
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
|
||||
|
@ -145,6 +150,10 @@ static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
|
|||
|
||||
static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
if (renderer->cache) {
|
||||
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
||||
}
|
||||
|
||||
switch (address) {
|
||||
case REG_DISPCNT:
|
||||
softwareRenderer->dispcnt = value;
|
||||
|
@ -199,52 +208,76 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
softwareRenderer->bg[3].y = value;
|
||||
break;
|
||||
case REG_BG2PA:
|
||||
GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
|
||||
softwareRenderer->bg[2].dx = value;
|
||||
break;
|
||||
case REG_BG2PB:
|
||||
GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
|
||||
softwareRenderer->bg[2].dmx = value;
|
||||
break;
|
||||
case REG_BG2PC:
|
||||
GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
|
||||
softwareRenderer->bg[2].dy = value;
|
||||
break;
|
||||
case REG_BG2PD:
|
||||
GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
|
||||
softwareRenderer->bg[2].dmy = value;
|
||||
break;
|
||||
case REG_BG2X_LO:
|
||||
GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
|
||||
if (softwareRenderer->bg[2].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[0][0]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG2X_HI:
|
||||
GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
|
||||
if (softwareRenderer->bg[2].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[0][0]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG2Y_LO:
|
||||
GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
|
||||
if (softwareRenderer->bg[2].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[0][1]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG2Y_HI:
|
||||
GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
|
||||
if (softwareRenderer->bg[2].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[0][1]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG3PA:
|
||||
GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
|
||||
softwareRenderer->bg[3].dx = value;
|
||||
break;
|
||||
case REG_BG3PB:
|
||||
GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
|
||||
softwareRenderer->bg[3].dmx = value;
|
||||
break;
|
||||
case REG_BG3PC:
|
||||
GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
|
||||
softwareRenderer->bg[3].dy = value;
|
||||
break;
|
||||
case REG_BG3PD:
|
||||
GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
|
||||
softwareRenderer->bg[3].dmy = value;
|
||||
break;
|
||||
case REG_BG3X_LO:
|
||||
GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
|
||||
if (softwareRenderer->bg[3].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[1][0]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG3X_HI:
|
||||
GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
|
||||
if (softwareRenderer->bg[3].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[1][0]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG3Y_LO:
|
||||
GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
|
||||
if (softwareRenderer->bg[3].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[1][1]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BG3Y_HI:
|
||||
GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
|
||||
if (softwareRenderer->bg[3].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[1][1]) {
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
break;
|
||||
case REG_BLDCNT:
|
||||
GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
|
||||
|
@ -342,39 +375,32 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
default:
|
||||
mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
|
||||
}
|
||||
softwareRenderer->nextIo[address >> 1] = value;
|
||||
if (softwareRenderer->cache[softwareRenderer->nextY].io[address >> 1] != value) {
|
||||
softwareRenderer->cache[softwareRenderer->nextY].io[address >> 1] = value;
|
||||
DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
softwareRenderer->oamDirty = 1;
|
||||
UNUSED(oam);
|
||||
softwareRenderer->oamDirty = 1;
|
||||
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
unsigned color = 0;
|
||||
color |= (value & 0x001F) << 11;
|
||||
color |= (value & 0x03E0) << 1;
|
||||
color |= (value & 0x7C00) >> 10;
|
||||
#else
|
||||
unsigned color = value;
|
||||
#endif
|
||||
#else
|
||||
unsigned color = 0;
|
||||
color |= (value << 3) & 0xF8;
|
||||
color |= (value << 6) & 0xF800;
|
||||
color |= (value << 9) & 0xF80000;
|
||||
color |= (color >> 5) & 0x070707;
|
||||
#endif
|
||||
color_t color = mColorFrom555(value);
|
||||
softwareRenderer->normalPalette[address >> 1] = color;
|
||||
if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
|
||||
softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
|
||||
|
@ -382,8 +408,9 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render
|
|||
softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
mCacheSetWritePalette(renderer->cache, address >> 1, color);
|
||||
}
|
||||
memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
|
||||
}
|
||||
|
||||
static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
|
||||
|
@ -487,6 +514,33 @@ static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
|
|||
static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
|
||||
if (y == VIDEO_VERTICAL_PIXELS - 1) {
|
||||
softwareRenderer->nextY = 0;
|
||||
} else {
|
||||
softwareRenderer->nextY = y + 1;
|
||||
}
|
||||
|
||||
bool dirty = softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F));
|
||||
if (memcmp(softwareRenderer->nextIo, softwareRenderer->cache[y].io, sizeof(softwareRenderer->nextIo))) {
|
||||
memcpy(softwareRenderer->cache[y].io, softwareRenderer->nextIo, sizeof(softwareRenderer->nextIo));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
softwareRenderer->cache[y].scale[0][0] = softwareRenderer->bg[2].sx;
|
||||
softwareRenderer->cache[y].scale[0][1] = softwareRenderer->bg[2].sy;
|
||||
softwareRenderer->cache[y].scale[1][0] = softwareRenderer->bg[3].sx;
|
||||
softwareRenderer->cache[y].scale[1][1] = softwareRenderer->bg[3].sy;
|
||||
|
||||
if (!dirty) {
|
||||
if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
|
||||
softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
|
||||
softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
|
||||
softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
|
||||
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||
if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
|
||||
int x;
|
||||
|
@ -592,21 +646,22 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
}
|
||||
|
||||
#ifdef COLOR_16_BIT
|
||||
#if defined(__ARM_NEON) && !defined(__APPLE__)
|
||||
_to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
|
||||
#else
|
||||
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
|
||||
for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
|
||||
row[x] = softwareRenderer->row[x];
|
||||
row[x + 1] = softwareRenderer->row[x + 1];
|
||||
row[x + 2] = softwareRenderer->row[x + 2];
|
||||
row[x + 3] = softwareRenderer->row[x + 3];
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
|
||||
#endif
|
||||
CLEAN_SCANLINE(softwareRenderer, y);
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
|
||||
|
||||
softwareRenderer->nextY = 0;
|
||||
if (softwareRenderer->temporaryBuffer) {
|
||||
mappedMemoryFree(softwareRenderer->temporaryBuffer, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
|
||||
softwareRenderer->temporaryBuffer = 0;
|
||||
|
@ -651,22 +706,6 @@ static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer*
|
|||
bg->size = GBARegisterBGCNTGetSize(value);
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
bg->dx = value;
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
bg->dmx = value;
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
bg->dy = value;
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
bg->dmy = value;
|
||||
}
|
||||
|
||||
static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
|
||||
bg->refx = (bg->refx & 0xFFFF0000) | value;
|
||||
bg->sx = bg->refx;
|
||||
|
|
|
@ -147,7 +147,7 @@ size_t GBASavedataSize(struct GBASavedata* savedata) {
|
|||
case SAVEDATA_FLASH1M:
|
||||
return SIZE_CART_FLASH1M;
|
||||
case SAVEDATA_EEPROM:
|
||||
return SIZE_CART_EEPROM;
|
||||
return (savedata->vf && savedata->vf->size(savedata->vf) == SIZE_CART_EEPROM512) ? SIZE_CART_EEPROM512 : SIZE_CART_EEPROM;
|
||||
case SAVEDATA_FORCE_NONE:
|
||||
return 0;
|
||||
case SAVEDATA_AUTODETECT:
|
||||
|
@ -257,20 +257,23 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata, bool realisticTiming) {
|
|||
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
|
||||
return;
|
||||
}
|
||||
int32_t eepromSize = SIZE_CART_EEPROM512;
|
||||
off_t end;
|
||||
if (!savedata->vf) {
|
||||
end = 0;
|
||||
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
||||
} else {
|
||||
end = savedata->vf->size(savedata->vf);
|
||||
if (end < SIZE_CART_EEPROM) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
||||
if (end < SIZE_CART_EEPROM512) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM512);
|
||||
} else if (end > SIZE_CART_EEPROM512) {
|
||||
eepromSize = SIZE_CART_EEPROM;
|
||||
}
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||
savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
|
||||
}
|
||||
savedata->realisticTiming = realisticTiming;
|
||||
if (end < SIZE_CART_EEPROM) {
|
||||
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
|
||||
if (end < SIZE_CART_EEPROM512) {
|
||||
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM512 - end);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,6 +408,19 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8
|
|||
}
|
||||
}
|
||||
|
||||
static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
|
||||
if (size < SIZE_CART_EEPROM512) {
|
||||
return;
|
||||
}
|
||||
if (!savedata->vf || savedata->vf->size(savedata->vf) > SIZE_CART_EEPROM512) {
|
||||
return;
|
||||
}
|
||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512);
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||
memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512);
|
||||
}
|
||||
|
||||
void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
|
||||
switch (savedata->command) {
|
||||
// Read header
|
||||
|
@ -430,6 +446,7 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
|
|||
} else if (writeSize == 1) {
|
||||
savedata->command = EEPROM_COMMAND_NULL;
|
||||
} else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
|
||||
_ensureEeprom(savedata, savedata->writeAddress >> 3);
|
||||
uint8_t current = savedata->data[savedata->writeAddress >> 3];
|
||||
current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
|
||||
current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
|
||||
|
@ -471,6 +488,7 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
|
|||
if (savedata->readBitsRemaining < 64) {
|
||||
int step = 63 - savedata->readBitsRemaining;
|
||||
uint32_t address = (savedata->readAddress + step) >> 3;
|
||||
_ensureEeprom(savedata, address);
|
||||
if (address >= SIZE_CART_EEPROM) {
|
||||
mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
|
||||
return 0xFF;
|
||||
|
|
|
@ -128,6 +128,8 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
return false;
|
||||
}
|
||||
gba->timing.root = NULL;
|
||||
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
|
||||
|
@ -185,5 +187,9 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
if (gba->rr) {
|
||||
gba->rr->stateLoaded(gba->rr, state);
|
||||
}
|
||||
|
||||
gba->timing.reroot = gba->timing.root;
|
||||
gba->timing.root = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,943 @@
|
|||
/* Copyright (c) 2013-2017 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/test/suite.h"
|
||||
|
||||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/gba/core.h>
|
||||
#include <mgba/internal/gba/cheats.h>
|
||||
|
||||
#include "gba/cheats/parv3.h"
|
||||
#include "gba/cheats/gameshark.h"
|
||||
|
||||
M_TEST_SUITE_SETUP(GBACheats) {
|
||||
struct mCore* core = GBACoreCreate();
|
||||
core->init(core);
|
||||
core->cheatDevice(core);
|
||||
*state = core;
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_SUITE_TEARDOWN(GBACheats) {
|
||||
if (!*state) {
|
||||
return 0;
|
||||
}
|
||||
struct mCore* core = *state;
|
||||
core->deinit(core);
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(createSet) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(addRawPARv3) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "80000000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_false(set->addLine(set, "43000000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3Assign) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300000 00000078", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "02300002 00005678", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "04300004 12345678", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead16(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead32(core, 0x03000004, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x78);
|
||||
assert_int_equal(core->rawRead16(core, 0x03000002, -1), 0x5678);
|
||||
assert_int_equal(core->rawRead32(core, 0x03000004, -1), 0x12345678);
|
||||
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3If1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300001 00000011", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300001 00000012", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x12);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11);
|
||||
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3If1x1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3If2) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300001 00000011", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "48300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300001 00000012", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x12);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3If2x2) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "48300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "48300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3If2Contain1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "48300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
set->deinit(set);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfX) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300001 00000011", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300001 00000012", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x12);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXxX) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXElse) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300001 00000011", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300001 00000012", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x12);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXElsexX) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXElsexXElse) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300006 00000061", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300006 00000062", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x61);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXContain1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXContain1Else) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXElseContain1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300002 00000021", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300002 00000022", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(doPARv3IfXContain1ElseContain1) {
|
||||
struct mCore* core = *state;
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
assert_non_null(device);
|
||||
struct mCheatSet* set = device->createSet(device, NULL);
|
||||
assert_non_null(set);
|
||||
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
|
||||
assert_true(set->addLine(set, "00300003 00000031", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000041", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000051", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300006 00000061", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300007 00000071", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300008 00000081", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "88300000 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300003 00000032", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300004 00000042", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300005 00000052", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 60000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300006 00000062", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "08300002 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300007 00000072", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300008 00000082", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00000000 40000000", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
assert_true(set->addLine(set, "00300000 00000001", GBA_CHEAT_PRO_ACTION_REPLAY));
|
||||
|
||||
core->reset(core);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0);
|
||||
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x61);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x81);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x72);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x61);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x81);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000002, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x61);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x81);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x72);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000002, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000002, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x61);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x81);
|
||||
|
||||
core->reset(core);
|
||||
core->rawWrite8(core, 0x03000000, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000001, -1, 0x1);
|
||||
core->rawWrite8(core, 0x03000002, -1, 0x1);
|
||||
mCheatRefresh(device, set);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x1);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
|
||||
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats,
|
||||
cmocka_unit_test(createSet),
|
||||
cmocka_unit_test(addRawPARv3),
|
||||
cmocka_unit_test(doPARv3Assign),
|
||||
cmocka_unit_test(doPARv3If1),
|
||||
cmocka_unit_test(doPARv3If1x1),
|
||||
cmocka_unit_test(doPARv3If2),
|
||||
cmocka_unit_test(doPARv3If2x2),
|
||||
cmocka_unit_test(doPARv3If2Contain1),
|
||||
cmocka_unit_test(doPARv3IfX),
|
||||
cmocka_unit_test(doPARv3IfXxX),
|
||||
cmocka_unit_test(doPARv3IfXElse),
|
||||
cmocka_unit_test(doPARv3IfXElsexX),
|
||||
cmocka_unit_test(doPARv3IfXElsexXElse),
|
||||
cmocka_unit_test(doPARv3IfXContain1),
|
||||
cmocka_unit_test(doPARv3IfXContain1Else),
|
||||
cmocka_unit_test(doPARv3IfXElseContain1),
|
||||
cmocka_unit_test(doPARv3IfXContain1ElseContain1))
|
|
@ -1,14 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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/test/suite.h"
|
||||
|
||||
M_TEST_SUITE_DECLARE(GBACore);
|
||||
|
||||
int TestRunGBA(void) {
|
||||
int failures = 0;
|
||||
failures += M_TEST_SUITE_RUN(GBACore);
|
||||
return failures;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/* Copyright (c) 2013-2016 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 TEST_GBA_H
|
||||
#define TEST_GBA_H
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
int TestRunGBA(void);
|
||||
|
||||
#endif
|
|
@ -6,11 +6,12 @@
|
|||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
#include <mgba/core/sync.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/internal/arm/macros.h>
|
||||
#include <mgba/internal/gba/dma.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/renderers/cache-set.h>
|
||||
#include <mgba/internal/gba/serialize.h>
|
||||
|
||||
#include <mgba-util/memory.h>
|
||||
|
@ -157,9 +158,9 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
||||
}
|
||||
GBAFrameEnded(video->p);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
--video->frameskipCounter;
|
||||
if (video->frameskipCounter < 0) {
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
video->frameskipCounter = video->frameskip;
|
||||
}
|
||||
++video->frameCounter;
|
||||
|
@ -214,7 +215,9 @@ static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
|
|||
}
|
||||
|
||||
static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
UNUSED(renderer);
|
||||
if (renderer->cache) {
|
||||
GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
|
||||
}
|
||||
switch (address) {
|
||||
case REG_BG0CNT:
|
||||
case REG_BG1CNT:
|
||||
|
@ -252,14 +255,13 @@ static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer*
|
|||
|
||||
static void GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
mCacheSetWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
UNUSED(value);
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,9 +70,8 @@ void LR35902Reset(struct LR35902Core* cpu) {
|
|||
cpu->irqh.reset(cpu);
|
||||
}
|
||||
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector) {
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu) {
|
||||
cpu->irqPending = true;
|
||||
cpu->irqVector = vector;
|
||||
}
|
||||
|
||||
static void _LR35902InstructionIRQStall(struct LR35902Core* cpu) {
|
||||
|
@ -85,18 +84,19 @@ static void _LR35902InstructionIRQFinish(struct LR35902Core* cpu) {
|
|||
}
|
||||
|
||||
static void _LR35902InstructionIRQDelay(struct LR35902Core* cpu) {
|
||||
cpu->index = cpu->sp + 1;
|
||||
cpu->bus = cpu->pc >> 8;
|
||||
--cpu->sp;
|
||||
cpu->index = cpu->sp;
|
||||
cpu->bus = cpu->pc;
|
||||
cpu->executionState = LR35902_CORE_MEMORY_STORE;
|
||||
cpu->instruction = _LR35902InstructionIRQFinish;
|
||||
cpu->pc = cpu->irqVector;
|
||||
cpu->pc = cpu->irqh.irqVector(cpu);
|
||||
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
||||
}
|
||||
|
||||
static void _LR35902InstructionIRQ(struct LR35902Core* cpu) {
|
||||
cpu->sp -= 2; /* TODO: Atomic incrementing? */
|
||||
--cpu->sp;
|
||||
cpu->index = cpu->sp;
|
||||
cpu->bus = cpu->pc;
|
||||
cpu->bus = cpu->pc >> 8;
|
||||
cpu->executionState = LR35902_CORE_MEMORY_STORE;
|
||||
cpu->instruction = _LR35902InstructionIRQDelay;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,11 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format" PARENT_SCOPE)
|
|||
set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 IOAPI_NO_64)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
list(APPEND OS_LIB citro3d ctru)
|
||||
if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo)
|
||||
list(APPEND OS_LIB citro3dd ctrud)
|
||||
else()
|
||||
list(APPEND OS_LIB citro3d ctru)
|
||||
endif()
|
||||
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/3ds-*.c ${CMAKE_CURRENT_SOURCE_DIR}/ctru-heap.c ${CMAKE_CURRENT_SOURCE_DIR}/socket.c)
|
||||
set(OS_SRC ${OS_SRC} PARENT_SCOPE)
|
||||
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
|
||||
|
|
|
@ -26,21 +26,21 @@ struct ctrUIVertex {
|
|||
float rotate[2];
|
||||
};
|
||||
|
||||
#define MAX_NUM_QUADS 256
|
||||
#define MAX_NUM_QUADS 4096
|
||||
#define VERTEX_BUFFER_SIZE MAX_NUM_QUADS * sizeof(struct ctrUIVertex)
|
||||
|
||||
static struct ctrUIVertex* ctrVertexBuffer = NULL;
|
||||
static int ctrNumVerts = 0;
|
||||
static int ctrVertStart = 0;
|
||||
|
||||
static C3D_Tex* activeTexture = NULL;
|
||||
static const C3D_Tex* activeTexture = NULL;
|
||||
|
||||
static shaderProgram_s uiProgram;
|
||||
static DVLB_s* uiShader = NULL;
|
||||
static int GSH_FVEC_projectionMtx;
|
||||
static int GSH_FVEC_textureMtx;
|
||||
|
||||
bool ctrInitGpu() {
|
||||
bool ctrInitGpu(void) {
|
||||
// Load vertex shader binary
|
||||
uiShader = DVLB_ParseFile((u32*) uishader, uishader_size);
|
||||
if (uiShader == NULL) {
|
||||
|
@ -83,7 +83,7 @@ bool ctrInitGpu() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ctrDeinitGpu() {
|
||||
void ctrDeinitGpu(void) {
|
||||
if (ctrVertexBuffer) {
|
||||
linearFree(ctrVertexBuffer);
|
||||
ctrVertexBuffer = NULL;
|
||||
|
@ -108,10 +108,23 @@ void ctrSetViewportSize(s16 w, s16 h, bool tilt) {
|
|||
C3D_FVUnifMtx4x4(GPU_GEOMETRY_SHADER, GSH_FVEC_projectionMtx, &projectionMtx);
|
||||
}
|
||||
|
||||
void ctrActivateTexture(C3D_Tex* texture) {
|
||||
void ctrFlushBatch(void) {
|
||||
int thisBatch = ctrNumVerts - ctrVertStart;
|
||||
if (!thisBatch) {
|
||||
return;
|
||||
}
|
||||
if (thisBatch < 0) {
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
C3D_DrawArrays(GPU_GEOMETRY_PRIM, ctrVertStart, thisBatch);
|
||||
ctrVertStart = ctrNumVerts;
|
||||
}
|
||||
|
||||
void ctrActivateTexture(const C3D_Tex* texture) {
|
||||
if (texture == activeTexture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeTexture) {
|
||||
ctrFlushBatch();
|
||||
}
|
||||
|
@ -172,14 +185,11 @@ void ctrAddRectEx(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s
|
|||
return;
|
||||
}
|
||||
|
||||
if (ctrNumVerts + ctrVertStart == MAX_NUM_QUADS) {
|
||||
ctrFlushBatch();
|
||||
C3D_Flush();
|
||||
ctrNumVerts = 0;
|
||||
ctrVertStart = 0;
|
||||
if (ctrNumVerts == MAX_NUM_QUADS) {
|
||||
abort();
|
||||
}
|
||||
|
||||
struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrVertStart + ctrNumVerts];
|
||||
struct ctrUIVertex* vtx = &ctrVertexBuffer[ctrNumVerts];
|
||||
vtx->x = x;
|
||||
vtx->y = y;
|
||||
vtx->w = w;
|
||||
|
@ -199,25 +209,17 @@ void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h) {
|
|||
ctrAddRectEx(color, x, y, w, h, u, v, w, h, 0);
|
||||
}
|
||||
|
||||
void ctrFlushBatch(void) {
|
||||
if (ctrNumVerts == 0) {
|
||||
return;
|
||||
}
|
||||
void ctrStartFrame(void) {
|
||||
ctrNumVerts = 0;
|
||||
ctrVertStart = 0;
|
||||
activeTexture = NULL;
|
||||
|
||||
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
|
||||
BufInfo_Init(bufInfo);
|
||||
BufInfo_Add(bufInfo, &ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex), 4, 0x3210);
|
||||
|
||||
GSPGPU_FlushDataCache(&ctrVertexBuffer[ctrVertStart], sizeof(struct ctrUIVertex) * ctrNumVerts);
|
||||
C3D_DrawArrays(GPU_GEOMETRY_PRIM, 0, ctrNumVerts);
|
||||
|
||||
ctrVertStart += ctrNumVerts;
|
||||
ctrNumVerts = 0;
|
||||
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 4, 0x3210);
|
||||
}
|
||||
|
||||
void ctrFinalize(void) {
|
||||
void ctrEndFrame(void) {
|
||||
ctrFlushBatch();
|
||||
C3D_Flush();
|
||||
ctrNumVerts = 0;
|
||||
ctrVertStart = 0;
|
||||
GSPGPU_FlushDataCache(ctrVertexBuffer, sizeof(struct ctrUIVertex) * ctrNumVerts);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@ void ctrDeinitGpu(void);
|
|||
|
||||
void ctrSetViewportSize(s16 w, s16 h, bool tilt);
|
||||
|
||||
void ctrActivateTexture(C3D_Tex* texture);
|
||||
void ctrActivateTexture(const C3D_Tex* texture);
|
||||
void ctrTextureMultiply(void);
|
||||
void ctrTextureBias(u32 color);
|
||||
void ctrAddRectEx(u32 color, s16 x, s16 y, s16 w, s16 h, s16 u, s16 v, s16 uw, s16 vh, float rotate);
|
||||
void ctrAddRect(u32 color, s16 x, s16 y, s16 u, s16 v, s16 w, s16 h);
|
||||
void ctrFlushBatch(void);
|
||||
void ctrFinalize(void);
|
||||
void ctrStartFrame(void);
|
||||
void ctrEndFrame(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -92,10 +92,15 @@ static C3D_Tex outputTexture;
|
|||
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
|
||||
static int bufferId = 0;
|
||||
static bool frameLimiter = true;
|
||||
static u64 tickCounter;
|
||||
|
||||
static C3D_RenderBuf bottomScreen;
|
||||
static C3D_RenderBuf topScreen;
|
||||
static C3D_RenderBuf upscaleBuffer;
|
||||
static C3D_RenderTarget* topScreen[2];
|
||||
static C3D_RenderTarget* bottomScreen[2];
|
||||
static int doubleBuffer = 0;
|
||||
static bool frameStarted = false;
|
||||
|
||||
static C3D_RenderTarget* upscaleBuffer;
|
||||
static C3D_Tex upscaleBufferTex;
|
||||
|
||||
static aptHookCookie cookie;
|
||||
|
||||
|
@ -106,10 +111,24 @@ static bool _initGpu(void) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!C3D_RenderBufInit(&topScreen, 240, 400, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&bottomScreen, 240, 320, GPU_RB_RGB8, 0) || !C3D_RenderBufInit(&upscaleBuffer, 512, 512, GPU_RB_RGB8, 0)) {
|
||||
topScreen[0] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||
topScreen[1] = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, 0);
|
||||
bottomScreen[0] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||
bottomScreen[1] = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, 0);
|
||||
if (!topScreen[0] || !topScreen[1] || !bottomScreen[0] || !bottomScreen[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!C3D_TexInitVRAM(&upscaleBufferTex, 512, 512, GPU_RB_RGB8)) {
|
||||
return false;
|
||||
}
|
||||
upscaleBuffer = C3D_RenderTargetCreateFromTex(&upscaleBufferTex, GPU_TEXFACE_2D, 0, 0);
|
||||
if (!upscaleBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
C3D_RenderTargetSetClear(upscaleBuffer, C3D_CLEAR_COLOR, 0, 0);
|
||||
|
||||
return ctrInitGpu();
|
||||
}
|
||||
|
||||
|
@ -120,9 +139,13 @@ static void _cleanup(void) {
|
|||
linearFree(outputBuffer);
|
||||
}
|
||||
|
||||
C3D_RenderBufDelete(&topScreen);
|
||||
C3D_RenderBufDelete(&bottomScreen);
|
||||
C3D_RenderBufDelete(&upscaleBuffer);
|
||||
C3D_RenderTargetDelete(topScreen[0]);
|
||||
C3D_RenderTargetDelete(topScreen[1]);
|
||||
C3D_RenderTargetDelete(bottomScreen[0]);
|
||||
C3D_RenderTargetDelete(bottomScreen[1]);
|
||||
C3D_RenderTargetDelete(upscaleBuffer);
|
||||
C3D_TexDelete(&upscaleBufferTex);
|
||||
C3D_TexDelete(&outputTexture);
|
||||
C3D_Fini();
|
||||
|
||||
gfxExit();
|
||||
|
@ -204,18 +227,41 @@ static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, voi
|
|||
static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right);
|
||||
|
||||
static void _drawStart(void) {
|
||||
C3D_RenderBufClear(&bottomScreen);
|
||||
C3D_RenderBufClear(&topScreen);
|
||||
}
|
||||
|
||||
static void _frameStart(void) {
|
||||
if (frameStarted) {
|
||||
return;
|
||||
}
|
||||
frameStarted = true;
|
||||
u8 flags = 0;
|
||||
if (!frameLimiter) {
|
||||
if (tickCounter + 4481000 > svcGetSystemTick()) {
|
||||
flags = C3D_FRAME_NONBLOCK;
|
||||
} else {
|
||||
tickCounter = svcGetSystemTick();
|
||||
}
|
||||
}
|
||||
C3D_FrameBegin(flags);
|
||||
// Mark both buffers used to make sure they get cleared
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
ctrStartFrame();
|
||||
}
|
||||
|
||||
static void _drawEnd(void) {
|
||||
ctrFinalize();
|
||||
C3D_RenderBufTransfer(&topScreen, (u32*) gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderBufTransfer(&bottomScreen, (u32*) gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
gfxSwapBuffersGpu();
|
||||
if (frameLimiter) {
|
||||
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
|
||||
if (!frameStarted) {
|
||||
return;
|
||||
}
|
||||
ctrEndFrame();
|
||||
C3D_RenderTargetSetOutput(topScreen[doubleBuffer], GFX_TOP, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_RenderTargetSetOutput(bottomScreen[doubleBuffer], GFX_BOTTOM, GFX_LEFT, GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
|
||||
C3D_FrameEnd(GX_CMDLIST_FLUSH);
|
||||
frameStarted = false;
|
||||
|
||||
doubleBuffer ^= 1;
|
||||
C3D_FrameBufClear(&bottomScreen[doubleBuffer]->frameBuf, C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameBufClear(&topScreen[doubleBuffer]->frameBuf, C3D_CLEAR_COLOR, 0, 0);
|
||||
}
|
||||
|
||||
static int _batteryState(void) {
|
||||
|
@ -234,12 +280,8 @@ static int _batteryState(void) {
|
|||
}
|
||||
|
||||
static void _guiPrepare(void) {
|
||||
int screen = screenMode < SM_PA_TOP ? GFX_BOTTOM : GFX_TOP;
|
||||
if (screen == GFX_BOTTOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
C3D_RenderBufBind(&bottomScreen);
|
||||
_frameStart();
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
ctrSetViewportSize(320, 240, true);
|
||||
}
|
||||
|
||||
|
@ -254,6 +296,7 @@ static void _resetCamera(struct m3DSImageSource* imageSource) {
|
|||
CAMU_SetSize(imageSource->cam, SIZE_QCIF, CONTEXT_A);
|
||||
CAMU_SetOutputFormat(imageSource->cam, OUTPUT_RGB_565, CONTEXT_A);
|
||||
CAMU_SetFrameRate(imageSource->cam, FRAME_RATE_30);
|
||||
CAMU_FlipImage(imageSource->cam, FLIP_NONE, CONTEXT_A);
|
||||
|
||||
CAMU_SetNoiseFilter(imageSource->cam, true);
|
||||
CAMU_SetAutoExposure(imageSource->cam, false);
|
||||
|
@ -261,9 +304,8 @@ static void _resetCamera(struct m3DSImageSource* imageSource) {
|
|||
}
|
||||
|
||||
static void _setup(struct mGUIRunner* runner) {
|
||||
bool isNew3DS = false;
|
||||
APT_CheckNew3DS(&isNew3DS);
|
||||
if (isNew3DS && !envIsHomebrew()) {
|
||||
uint8_t mask;
|
||||
if (R_SUCCEEDED(svcGetProcessAffinityMask(&mask, CUR_PROCESS_HANDLE, 4)) && mask >= 4) {
|
||||
mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1);
|
||||
mCoreLoadForeignConfig(runner->core, &runner->config);
|
||||
}
|
||||
|
@ -285,7 +327,7 @@ static void _setup(struct mGUIRunner* runner) {
|
|||
_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L);
|
||||
_map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R);
|
||||
|
||||
outputBuffer = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * 2, 0x80);
|
||||
outputBuffer = linearMemAlign(256 * 224 * 2, 0x80);
|
||||
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
|
||||
|
||||
unsigned mode;
|
||||
|
@ -295,9 +337,9 @@ static void _setup(struct mGUIRunner* runner) {
|
|||
if (mCoreConfigGetUIntValue(&runner->config, "filterMode", &mode) && mode < FM_MAX) {
|
||||
filterMode = mode;
|
||||
if (filterMode == FM_NEAREST) {
|
||||
C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_NEAREST, GPU_NEAREST);
|
||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_NEAREST, GPU_NEAREST);
|
||||
} else {
|
||||
C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR);
|
||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR);
|
||||
}
|
||||
}
|
||||
if (mCoreConfigGetUIntValue(&runner->config, "darkenMode", &mode) && mode < DM_MAX) {
|
||||
|
@ -354,9 +396,9 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
|||
if (mCoreConfigGetUIntValue(&runner->config, "filterMode", &mode) && mode < FM_MAX) {
|
||||
filterMode = mode;
|
||||
if (filterMode == FM_NEAREST) {
|
||||
C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_NEAREST, GPU_NEAREST);
|
||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_NEAREST, GPU_NEAREST);
|
||||
} else {
|
||||
C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR);
|
||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR);
|
||||
}
|
||||
}
|
||||
if (mCoreConfigGetUIntValue(&runner->config, "darkenMode", &mode) && mode < DM_MAX) {
|
||||
|
@ -419,22 +461,23 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
|
|||
}
|
||||
|
||||
static void _drawTex(struct mCore* core, bool faded) {
|
||||
_frameStart();
|
||||
unsigned screen_w, screen_h;
|
||||
switch (screenMode) {
|
||||
case SM_PA_BOTTOM:
|
||||
C3D_RenderBufBind(&bottomScreen);
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
screen_w = 320;
|
||||
screen_h = 240;
|
||||
break;
|
||||
case SM_PA_TOP:
|
||||
C3D_RenderBufBind(&topScreen);
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
screen_w = 400;
|
||||
screen_h = 240;
|
||||
break;
|
||||
default:
|
||||
C3D_RenderBufBind(&upscaleBuffer);
|
||||
screen_w = upscaleBuffer.colorBuf.width;
|
||||
screen_h = upscaleBuffer.colorBuf.height;
|
||||
C3D_FrameDrawOn(upscaleBuffer);
|
||||
screen_w = 512;
|
||||
screen_h = 512;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -523,10 +566,10 @@ static void _drawTex(struct mCore* core, bool faded) {
|
|||
coreh = h;
|
||||
screen_h = 240;
|
||||
if (screenMode < SM_PA_TOP) {
|
||||
C3D_RenderBufBind(&bottomScreen);
|
||||
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
|
||||
screen_w = 320;
|
||||
} else {
|
||||
C3D_RenderBufBind(&topScreen);
|
||||
C3D_FrameDrawOn(topScreen[doubleBuffer]);
|
||||
screen_w = 400;
|
||||
}
|
||||
ctrSetViewportSize(screen_w, screen_h, true);
|
||||
|
@ -555,7 +598,7 @@ static void _drawTex(struct mCore* core, bool faded) {
|
|||
|
||||
x = (screen_w - w) / 2;
|
||||
y = (screen_h - h) / 2;
|
||||
ctrActivateTexture(&upscaleBuffer.colorBuf);
|
||||
ctrActivateTexture(&upscaleBufferTex);
|
||||
ctrAddRectEx(0xFFFFFFFF, x, y, w, h, 0, 0, corew, coreh, 0);
|
||||
ctrFlushBatch();
|
||||
}
|
||||
|
@ -622,13 +665,17 @@ static void _incrementScreenMode(struct mGUIRunner* runner) {
|
|||
screenMode = (screenMode + 1) % SM_MAX;
|
||||
mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
|
||||
|
||||
C3D_RenderBufClear(&bottomScreen);
|
||||
C3D_RenderBufClear(&topScreen);
|
||||
C3D_FrameBufClear(&bottomScreen[doubleBuffer]->frameBuf, C3D_CLEAR_COLOR, 0, 0);
|
||||
C3D_FrameBufClear(&topScreen[doubleBuffer]->frameBuf, C3D_CLEAR_COLOR, 0, 0);
|
||||
}
|
||||
|
||||
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
||||
UNUSED(runner);
|
||||
if (frameLimiter == limit) {
|
||||
return;
|
||||
}
|
||||
frameLimiter = limit;
|
||||
tickCounter = svcGetSystemTick();
|
||||
}
|
||||
|
||||
static uint32_t _pollInput(const struct mInputMap* map) {
|
||||
|
@ -694,7 +741,6 @@ static void _startRequestImage(struct mImageSource* source, unsigned w, unsigned
|
|||
CAMU_Activate(imageSource->cam);
|
||||
CAMU_ClearBuffer(PORT_CAM1);
|
||||
CAMU_StartCapture(PORT_CAM1);
|
||||
CAMU_PlayShutterSound(SHUTTER_SOUND_TYPE_MOVIE);
|
||||
|
||||
if (imageSource->cam) {
|
||||
CAMU_SetReceiving(&imageSource->handles[0], imageSource->buffer, PORT_CAM1, imageSource->bufferSize, imageSource->transferSize);
|
||||
|
@ -845,7 +891,7 @@ int main() {
|
|||
}
|
||||
C3D_TexSetWrap(&outputTexture, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
|
||||
C3D_TexSetFilter(&outputTexture, GPU_NEAREST, GPU_NEAREST);
|
||||
C3D_TexSetFilter(&upscaleBuffer.colorBuf, GPU_LINEAR, GPU_LINEAR);
|
||||
C3D_TexSetFilter(&upscaleBufferTex, GPU_LINEAR, GPU_LINEAR);
|
||||
void* outputTextureEnd = (u8*)outputTexture.data + 256 * 256 * 2;
|
||||
|
||||
// Zero texture data to make sure no garbage around the border interferes with filtering
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <mgba/core/blip_buf.h>
|
||||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/version.h>
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/gb/core.h>
|
||||
|
@ -101,8 +102,8 @@ void retro_set_environment(retro_environment_t env) {
|
|||
struct retro_variable vars[] = {
|
||||
{ "mgba_solar_sensor_level", "Solar sensor level; 0|1|2|3|4|5|6|7|8|9|10" },
|
||||
{ "mgba_allow_opposing_directions", "Allow opposing directional input; OFF|ON" },
|
||||
{ "mgba_use_bios", "Use BIOS file if found; ON|OFF" },
|
||||
{ "mgba_skip_bios", "Skip BIOS intro; OFF|ON" },
|
||||
{ "mgba_use_bios", "Use BIOS file if found (requires restart); ON|OFF" },
|
||||
{ "mgba_skip_bios", "Skip BIOS intro (requires restart); OFF|ON" },
|
||||
{ "mgba_idle_optimization", "Idle loop removal; Remove Known|Detect and Remove|Don't Remove" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
@ -390,7 +391,7 @@ bool retro_load_game(const struct retro_game_info* game) {
|
|||
core->init(core);
|
||||
core->setAVStream(core, &stream);
|
||||
|
||||
outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||
outputBuffer = malloc(256 * 224 * BYTES_PER_PIXEL);
|
||||
core->setVideoBuffer(core, outputBuffer, 256);
|
||||
|
||||
core->setAudioBufferSize(core, SAMPLES);
|
||||
|
@ -527,16 +528,9 @@ size_t retro_get_memory_size(unsigned id) {
|
|||
if (core->platform(core) == PLATFORM_GBA) {
|
||||
switch (((struct GBA*) core->board)->memory.savedata.type) {
|
||||
case SAVEDATA_AUTODETECT:
|
||||
case SAVEDATA_FLASH1M:
|
||||
return SIZE_CART_FLASH1M;
|
||||
case SAVEDATA_FLASH512:
|
||||
return SIZE_CART_FLASH512;
|
||||
case SAVEDATA_EEPROM:
|
||||
return SIZE_CART_EEPROM;
|
||||
case SAVEDATA_SRAM:
|
||||
return SIZE_CART_SRAM;
|
||||
case SAVEDATA_FORCE_NONE:
|
||||
return 0;
|
||||
default:
|
||||
return GBASavedataSize(&((struct GBA*) core->board)->memory.savedata);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -582,7 +576,12 @@ void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, con
|
|||
break;
|
||||
}
|
||||
#ifdef NDEBUG
|
||||
if (category == _mLOG_CAT_GBA_BIOS()) {
|
||||
static int biosCat = -1;
|
||||
if (biosCat < 0) {
|
||||
biosCat = mLogCategoryById("gba.bios");
|
||||
}
|
||||
|
||||
if (category == biosCat) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -65,13 +65,9 @@
|
|||
};
|
||||
mCoreConfigLoadDefaults(&core->config, &opts);
|
||||
core->init(core);
|
||||
outputBuffer = nil;
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
|
||||
core->setVideoBuffer(core, outputBuffer, width);
|
||||
core->setAudioBufferSize(core, SAMPLES);
|
||||
|
||||
cheatSets = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
|
@ -108,6 +104,15 @@
|
|||
mCoreAutoloadSave(core);
|
||||
|
||||
core->reset(core);
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
if (outputBuffer) {
|
||||
free(outputBuffer);
|
||||
}
|
||||
outputBuffer = malloc(width * height * BYTES_PER_PIXEL);
|
||||
core->setVideoBuffer(core, outputBuffer, width);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/configuration.h>
|
||||
#include <mgba-util/formatting.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/vector.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -190,13 +191,6 @@ static void mGLES2ContextClear(struct VideoBackend* v) {
|
|||
void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
||||
GLint viewport[4];
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);
|
||||
if (shader->blend) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
int drawW = shader->width;
|
||||
|
@ -222,6 +216,14 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
drawH -= drawH % context->d.height;
|
||||
}
|
||||
glViewport(padW, padH, drawW, drawH);
|
||||
if (shader->blend) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
if (shader->tex && (shader->width <= 0 || shader->height <= 0)) {
|
||||
GLint oldTex;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex);
|
||||
|
|
|
@ -120,7 +120,20 @@ static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) {
|
|||
|
||||
static void _vfsceTruncate(struct VFile* vf, size_t size) {
|
||||
struct VFileSce* vfsce = (struct VFileSce*) vf;
|
||||
// TODO
|
||||
SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR);
|
||||
SceOff end = sceIoLseek(vfsce->fd, 0, SEEK_END);
|
||||
if (end < size) {
|
||||
uint8_t buffer[2048] = {};
|
||||
size_t write = size - end;
|
||||
while (write >= sizeof(buffer)) {
|
||||
sceIoWrite(vfsce->fd, buffer, sizeof(buffer));
|
||||
write -= sizeof(buffer);
|
||||
}
|
||||
if (write) {
|
||||
sceIoWrite(vfsce->fd, buffer, write);
|
||||
}
|
||||
} // TODO: Else
|
||||
sceIoLseek(vfsce->fd, cur, SEEK_SET);
|
||||
}
|
||||
|
||||
ssize_t _vfsceSize(struct VFile* vf) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
/build
|
||||
/dist
|
||||
.eggs
|
||||
.cache
|
||||
*.egg-info*
|
|
@ -1,4 +1,7 @@
|
|||
find_program(PYTHON python)
|
||||
find_package(PythonLibs ${USE_PYTHON_VERSION})
|
||||
find_package(PythonInterp ${USE_PYTHON_VERSION})
|
||||
list(APPEND DEPENDENCY_LIB ${PYTHON_LIBRARIES})
|
||||
include_directories(AFTER ${PYTHON_INCLUDE_DIRS})
|
||||
|
||||
get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
|
||||
set(INCLUDE_FLAGS)
|
||||
|
@ -6,24 +9,22 @@ foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
|
|||
list(APPEND INCLUDE_FLAGS "-I${DIR}")
|
||||
endforeach()
|
||||
|
||||
include(FindPythonLibs)
|
||||
list(APPEND DEPENDENCY_LIB ${PYTHON_LIBRARIES})
|
||||
include_directories(AFTER ${PYTHON_INCLUDE_DIRS})
|
||||
|
||||
file(GLOB PYTHON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
if(NOT GIT_TAG)
|
||||
if(GIT_BRANCH STREQUAL "master" OR NOT GIT_BRANCH)
|
||||
set(PYLIB_VERSION -b -${GIT_REV}-${GIT_COMMIT_SHORT})
|
||||
else()
|
||||
set(PYLIB_VERSION -b -${GIT_BRANCH}-${GIT_REV}-${GIT_COMMIT_SHORT})
|
||||
endif()
|
||||
endif()
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${BINARY_NAME}
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
|
||||
DEPENDS ${PYTHON_HEADERS}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py)
|
||||
|
||||
add_custom_command(OUTPUT lib.c
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
||||
DEPENDS ${PYTHON_HEADERS}
|
||||
DEPENDS ${BINARY_NAME}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py)
|
||||
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lib.c PROPERTIES GENERATED ON)
|
||||
|
@ -32,6 +33,29 @@ file(GLOB PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
|||
add_library(${BINARY_NAME}-pylib STATIC ${CMAKE_CURRENT_BINARY_DIR}/lib.c ${PYTHON_SRC})
|
||||
set_target_properties(${BINARY_NAME}-pylib PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_BINARY_DIR};${INCLUDE_DIRECTORIES}")
|
||||
set_target_properties(${BINARY_NAME}-pylib PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
set(PYTHON_LIBRARY ${BINARY_NAME}-pylib PARENT_SCOPE)
|
||||
|
||||
add_custom_target(${BINARY_NAME}-py ALL DEPENDS ${BINARY_NAME}-pylib ${CMAKE_CURRENT_BINARY_DIR}/build/lib/${BINARY_NAME}/__init__.py)
|
||||
add_custom_target(${BINARY_NAME}-py ALL
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py egg_info -e ${CMAKE_CURRENT_BINARY_DIR} ${PYLIB_VERSION}
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build -b ${CMAKE_CURRENT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${BINARY_NAME}
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
|
||||
DEPENDS ${PYTHON_HEADERS}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
||||
DEPENDS ${BINARY_NAME}-pylib)
|
||||
|
||||
file(GLOB BASE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test_*.py)
|
||||
file(GLOB SUBTESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
|
||||
foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
||||
if(APPLE)
|
||||
set(PATH DYLD_LIBRARY_PATH)
|
||||
elseif(WIN32)
|
||||
set(PATH PATH)
|
||||
else()
|
||||
set(PATH LD_LIBRARY_PATH)
|
||||
endif()
|
||||
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/(tests/.*/)?test_" "" TEST_NAME "${TEST}")
|
||||
string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}")
|
||||
add_test(python-${TEST_NAME} ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST})
|
||||
set_tests_properties(python-${TEST_NAME} PROPERTIES ENVIRONMENT "${PATH}=${CMAKE_CURRENT_BINARY_DIR}/..")
|
||||
endforeach()
|
||||
|
|
|
@ -8,12 +8,20 @@
|
|||
|
||||
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
||||
#define DECL_BITFIELD(newtype, oldtype) typedef oldtype newtype
|
||||
#define DECL_BIT(type, name, bit)
|
||||
#define DECL_BITS(type, name, bit, nbits)
|
||||
#define DECL_BIT(type, field, bit) DECL_BITS(type, field, bit, 1)
|
||||
#define DECL_BITS(TYPE, FIELD, START, SIZE) \
|
||||
TYPE TYPE ## Is ## FIELD (TYPE); \
|
||||
TYPE TYPE ## Get ## FIELD (TYPE); \
|
||||
TYPE TYPE ## Clear ## FIELD (TYPE); \
|
||||
TYPE TYPE ## Fill ## FIELD (TYPE); \
|
||||
TYPE TYPE ## Set ## FIELD (TYPE, TYPE); \
|
||||
TYPE TYPE ## TestFill ## FIELD (TYPE, bool);
|
||||
|
||||
#define CXX_GUARD_START
|
||||
#define CXX_GUARD_END
|
||||
|
||||
#define PYCPARSE
|
||||
|
||||
typedef int... time_t;
|
||||
typedef int... off_t;
|
||||
typedef ... va_list;
|
||||
|
@ -24,13 +32,13 @@ typedef ...* png_unknown_chunkp;
|
|||
void free(void*);
|
||||
|
||||
#include <limits.h>
|
||||
#undef const
|
||||
|
||||
#include "flags.h"
|
||||
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/map-cache.h>
|
||||
#include <mgba/core/mem-search.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba/core/version.h>
|
||||
|
||||
|
@ -48,13 +56,13 @@ void free(void*);
|
|||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#include <mgba/internal/gba/renderers/tile-cache.h>
|
||||
#include <mgba/internal/gba/renderers/cache-set.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#include <mgba/internal/gb/renderers/tile-cache.h>
|
||||
#include <mgba/internal/gb/renderers/cache-set.h>
|
||||
#endif
|
||||
#ifdef USE_DEBUGGERS
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
|
|
@ -17,24 +17,27 @@ if __name__ == "__main__":
|
|||
cppflags.extend(["-I" + incdir, "-I" + srcdir, "-I" + bindir])
|
||||
|
||||
ffi.set_source("mgba._pylib", """
|
||||
#define static
|
||||
#define inline
|
||||
#include "flags.h"
|
||||
#define OPAQUE_THREADING
|
||||
#include <mgba/core/cache-set.h>
|
||||
#include <mgba-util/common.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/map-cache.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/mem-search.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba/internal/debugger/cli-debugger.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#include <mgba/internal/gba/renderers/tile-cache.h>
|
||||
#include <mgba/internal/gba/renderers/cache-set.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/renderers/tile-cache.h>
|
||||
#include <mgba/internal/gb/renderers/cache-set.h>
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -71,6 +74,11 @@ for line in preprocessed.splitlines():
|
|||
ffi.embedding_api('\n'.join(lines))
|
||||
|
||||
ffi.embedding_init_code("""
|
||||
import os, os.path
|
||||
venv = os.getenv('VIRTUAL_ENV')
|
||||
if venv:
|
||||
activate = os.path.join(venv, 'bin', 'activate_this.py')
|
||||
exec(compile(open(activate, "rb").read(), activate, 'exec'), dict(__file__=activate))
|
||||
from mgba._pylib import ffi, lib
|
||||
symbols = {}
|
||||
globalSyms = {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
from PIL.ImageChops import difference
|
||||
from PIL.ImageOps import autocontrast
|
||||
from PIL.Image import open as PIOpen
|
||||
|
||||
class VideoFrame(object):
|
||||
def __init__(self, pilImage):
|
||||
self.image = pilImage.convert('RGB')
|
||||
|
||||
@staticmethod
|
||||
def diff(a, b):
|
||||
diff = difference(a.image, b.image)
|
||||
diffNormalized = autocontrast(diff)
|
||||
return (VideoFrame(diff), VideoFrame(diffNormalized))
|
||||
|
||||
@staticmethod
|
||||
def load(path):
|
||||
with open(path, 'rb') as f:
|
||||
image = PIOpen(f)
|
||||
image.load()
|
||||
return VideoFrame(image)
|
||||
|
||||
def save(self, path):
|
||||
return self.image.save(path)
|
|
@ -0,0 +1,47 @@
|
|||
from mgba.image import Image
|
||||
from collections import namedtuple
|
||||
from . import VideoFrame
|
||||
|
||||
Output = namedtuple('Output', ['video'])
|
||||
|
||||
class Tracer(object):
|
||||
def __init__(self, core):
|
||||
self.core = core
|
||||
self.fb = Image(*core.desiredVideoDimensions())
|
||||
self.core.setVideoBuffer(self.fb)
|
||||
self._videoFifo = []
|
||||
|
||||
def yieldFrames(self, skip=0, limit=None):
|
||||
self.core.reset()
|
||||
skip = (skip or 0) + 1
|
||||
while skip > 0:
|
||||
frame = self.core.frameCounter
|
||||
self.core.runFrame()
|
||||
skip -= 1
|
||||
while frame <= self.core.frameCounter and limit != 0:
|
||||
self._videoFifo.append(VideoFrame(self.fb.toPIL()))
|
||||
yield frame
|
||||
frame = self.core.frameCounter
|
||||
self.core.runFrame()
|
||||
if limit is not None:
|
||||
assert limit >= 0
|
||||
limit -= 1
|
||||
|
||||
def video(self, generator=None, **kwargs):
|
||||
if not generator:
|
||||
generator = self.yieldFrames(**kwargs)
|
||||
try:
|
||||
while True:
|
||||
if self._videoFifo:
|
||||
result = self._videoFifo[0]
|
||||
self._videoFifo = self._videoFifo[1:]
|
||||
yield result
|
||||
else:
|
||||
next(generator)
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
def output(self, **kwargs):
|
||||
generator = self.yieldFrames(**kwargs)
|
||||
|
||||
return mCoreOutput(video=self.video(generator=generator, **kwargs))
|
|
@ -0,0 +1,96 @@
|
|||
import os, os.path
|
||||
import mgba.core, mgba.image
|
||||
import cinema.movie
|
||||
import itertools
|
||||
import glob
|
||||
import re
|
||||
import yaml
|
||||
from copy import deepcopy
|
||||
from cinema import VideoFrame
|
||||
from cinema.util import dictMerge
|
||||
|
||||
class CinemaTest(object):
|
||||
TEST = 'test.(mvl|gb|gba|nds)'
|
||||
|
||||
def __init__(self, path, root, settings={}):
|
||||
self.fullPath = path or []
|
||||
self.path = os.path.abspath(os.path.join(root, *self.fullPath))
|
||||
self.root = root
|
||||
self.name = '.'.join(path)
|
||||
self.settings = settings
|
||||
try:
|
||||
with open(os.path.join(self.path, 'manifest.yml'), 'r') as f:
|
||||
dictMerge(self.settings, yaml.safe_load(f))
|
||||
except IOError:
|
||||
pass
|
||||
self.tests = {}
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, self.name)
|
||||
|
||||
def setUp(self):
|
||||
results = [f for f in glob.glob(os.path.join(self.path, 'test.*')) if re.search(self.TEST, f)]
|
||||
self.core = mgba.core.loadPath(results[0])
|
||||
if 'config' in self.settings:
|
||||
self.config = mgba.core.Config(defaults=self.settings['config'])
|
||||
self.core.loadConfig(self.config)
|
||||
self.core.reset()
|
||||
|
||||
def addTest(self, name, cls=None, settings={}):
|
||||
cls = cls or self.__class__
|
||||
newSettings = deepcopy(self.settings)
|
||||
dictMerge(newSettings, settings)
|
||||
self.tests[name] = cls(self.fullPath + [name], self.root, newSettings)
|
||||
return self.tests[name]
|
||||
|
||||
def outputSettings(self):
|
||||
outputSettings = {}
|
||||
if 'frames' in self.settings:
|
||||
outputSettings['limit'] = self.settings['frames']
|
||||
if 'skip' in self.settings:
|
||||
outputSettings['skip'] = self.settings['skip']
|
||||
return outputSettings
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.path < other.path
|
||||
|
||||
class VideoTest(CinemaTest):
|
||||
BASELINE = 'baseline_%04u.png'
|
||||
|
||||
def setUp(self):
|
||||
super(VideoTest, self).setUp();
|
||||
self.tracer = cinema.movie.Tracer(self.core)
|
||||
|
||||
def generateFrames(self):
|
||||
for i, frame in zip(itertools.count(), self.tracer.video(**self.outputSettings())):
|
||||
try:
|
||||
baseline = VideoFrame.load(os.path.join(self.path, self.BASELINE % i))
|
||||
yield baseline, frame, VideoFrame.diff(baseline, frame)
|
||||
except IOError:
|
||||
yield None, frame, (None, None)
|
||||
|
||||
def test(self):
|
||||
self.baseline, self.frames, self.diffs = zip(*self.generateFrames())
|
||||
assert not any(any(diffs[0].image.convert("L").point(bool).getdata()) for diffs in self.diffs)
|
||||
|
||||
def generateBaseline(self):
|
||||
for i, frame in zip(itertools.count(), self.tracer.video(**self.outputSettings())):
|
||||
frame.save(os.path.join(self.path, self.BASELINE % i))
|
||||
|
||||
def gatherTests(root=os.getcwd()):
|
||||
tests = CinemaTest([], root)
|
||||
for path, _, files in os.walk(root):
|
||||
test = [f for f in files if re.match(CinemaTest.TEST, f)]
|
||||
if not test:
|
||||
continue
|
||||
prefix = os.path.commonprefix([path, root])
|
||||
suffix = path[len(prefix)+1:]
|
||||
testPath = suffix.split(os.sep)
|
||||
testRoot = tests
|
||||
for component in testPath[:-1]:
|
||||
newTest = testRoot.tests.get(component)
|
||||
if not newTest:
|
||||
newTest = testRoot.addTest(component)
|
||||
testRoot = newTest
|
||||
testRoot.addTest(testPath[-1], VideoTest)
|
||||
return tests
|
|
@ -0,0 +1,9 @@
|
|||
def dictMerge(a, b):
|
||||
for key, value in b.items():
|
||||
if isinstance(value, dict):
|
||||
if key in a:
|
||||
dictMerge(a[key], value)
|
||||
else:
|
||||
a[key] = dict(value)
|
||||
else:
|
||||
a[key] = value
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue