Merge branch 'master' into feature/input-revamp

This commit is contained in:
Vicki Pfau 2017-10-04 10:08:42 -07:00
commit 079ceead68
639 changed files with 25060 additions and 1964 deletions

67
CHANGES
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ struct mTimingEvent {
struct mTiming {
struct mTimingEvent* root;
struct mTimingEvent* reroot;
uint32_t masterCycles;
int32_t* relativeCycles;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ struct GBCartridgeOverride {
enum GBModel model;
enum GBMemoryBankControllerType mbc;
uint32_t gbColors[4];
uint32_t gbColors[12];
};
struct Configuration;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,8 @@ Jaime J. Denizard
Fog
Reilly Grant
Philip Horton
Jordan Jorgensen
mars
pr1ntf
Rohit Nirmal
Rhys Powell
rootfather

View File

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

62
src/core/cache-set.c Normal file
View File

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

View File

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

205
src/core/map-cache.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

943
src/gba/test/cheats.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

5
src/platform/python/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/build
/dist
.eggs
.cache
*.egg-info*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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