Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-11 02:12:43 -07:00
commit 0d93ff8844
90 changed files with 5975 additions and 3959 deletions

18
CHANGES
View File

@ -37,7 +37,11 @@ Misc:
Features:
- e-Reader card scanning
- Add WebP and APNG recording
- Add mute option in homebrew ports
- Add status indicators for fast-forward and mute in homebrew ports
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
- Support for unlicensed BBD Game Boy mapper
- Support for unlicensed Hitek Game Boy mapper
- Stack tracing tools in ARM debugger (by ahigerd)
- Command scripts for CLI debugger (by ahigerd)
Emulation fixes:
@ -47,15 +51,21 @@ Emulation fixes:
- GB Audio: Fix initial sweep state
- GB Audio: Fix serializing sweep time
- GB Audio: Fix deserializing audio channels 2 and 3
- GB Audio: Fix deserializing while audio was disabled (fixes mgba.io/i/1305)
- GB MBC: Fix MBC1 mode changing behavior
- GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716)
- GBA: Fix timing advancing too quickly in rare cases
- GBA Audio: Fix deserializing SOUNDCNT_L
- GBA Audio: Fix stereo in XQ audio
- GBA Audio: Fix volume/mute in XQ audio (fixes mgba.io/i/1864)
- GBA Audio: Revamp FIFO emulation (fixes mgba.io/i/356, mgba.io/i/875, mgba.io/i/1847)
- GBA BIOS: Implement dummy sound driver calls
- GBA BIOS: Improve HLE BIOS timing
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
- GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823)
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
- GBA DMA: Fix ordering and timing of overlapping DMAs
- GBA Hardware: Fix GB Player detection on big endian platforms
- GBA Memory: Improve gamepak prefetch timing
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
- GBA SIO: Fix copying Normal mode transfer values
@ -64,21 +74,27 @@ Emulation fixes:
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing
- GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846)
- GBA Video: Don't draw sprites using unmapped VRAM in GL renderer (fixes mgba.io/i/1865)
- SM83: Emulate HALT bug
Other fixes:
- 3DS: Redo video sync to be more precise
- 3DS: Fix crash with libctru 2.0 when exiting
- 3DS: Fix thread cleanup
- All: Improve export headers (fixes mgba.io/i/1738)
- Core: Ensure ELF regions can be written before trying
- Core: Fix reported ROM size when a fixed buffer size is used
- Core: Fix memory leak loading ELF files
- Debugger: Don't skip undefined instructions when debugger attached
- FFmpeg: Fix some small memory leaks
- FFmpeg: Fix encoding of time base
- GBA: Disable more checks when loading GS save with checks disabled (fixes mgba.io/i/1851)
- GBA: Fix endianness issues in renderer proxy
- GBA Core: Fix memory leak when loading symbols
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)
- Qt: Fix a race condition in the frame inspector
- Qt: Add dummy English translation file (fixes mgba.io/i/1469)
- Qt: Fix Battle Chip view not displaying chips on some DPI settings
- mGUI: Fix closing down a game if an exit is signalled
- mVL: Fix injecting accidentally draining non-injection buffer
- SM83: Simplify register pair access on big endian
@ -93,7 +109,9 @@ Misc:
- Qt: Renderer can be changed while a game is running
- Qt: Add hex index to palette view
- Qt: Add transformation matrix info to sprite view
- Qt: Memory viewer now supports editing decimal values directly (closes mgba.io/i/1705)
- Util: Reset vector size on deinit
- VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)
0.8.3: (2020-08-03)
Emulation fixes:

View File

@ -1137,7 +1137,7 @@ if(DISTBUILD)
elseif(WII)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-wii ${BINARY_NAME}-perf)
elseif(PSP2)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-psp2)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-psp2 ${BINARY_NAME}-perf)
elseif(SWITCH)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-switch)
endif()

View File

@ -53,21 +53,23 @@ The following mappers are fully supported:
- MBC7
- Wisdom Tree (unlicensed)
- Pokémon Jade/Diamond (unlicensed)
- BBD (unlicensed MBC5-like)
- Hitek (unlicensed MBC5-like)
The following mappers are partially supported:
- MBC6
- MBC6 (missing flash memory support)
- MMM01
- Pocket Cam
- TAMA5
- HuC-1
- HuC-3
- TAMA5 (missing RTC support)
- HuC-1 (missing IR support)
- HuC-3 (missing RTC and IR support)
### Planned features
- Networked multiplayer link cable support.
- Dolphin/JOY bus link cable support.
- M4A audio mixing, for higher quality sound than hardware.
- MP2k audio mixing, for higher quality sound than hardware.
- Re-recording support for tool-assist runs.
- Lua support for scripting.
- A comprehensive debug suite.

View File

@ -27,7 +27,7 @@ Features
- Unterstützung für Screenshots.
- Unterstützung für Cheat-Codes.
- 9 Speicherstände für Savestates/Spielzustände. Savestates können auch als Screenshots dargestellt werden.
- Video- und GIF-Aufzeichnung.
- Video-, GIF-, WebP- und APNG-Aufzeichnung.
- e-Reader-Unterstützung.
- Frei wählbare Tastenbelegungen für Tastaturen und Controller.
- Unterstützung für ZIP- und 7z-Archive.
@ -51,21 +51,24 @@ Die folgenden Mapper werden vollständig unterstützt:
- MBC5+Rumble (MBC5+Rüttel-Modul)
- MBC7
- Wisdom Tree (nicht lizenziert)
- Pokémon Jade/Diamond (nicht lizenziert)
- BBD (nicht lizenziert, ählich MBC5)
- Hitek (nicht lizenziert, ähnlich MBC5)
Die folgenden Mapper werden teilweise unterstützt:
- MBC6
- MBC6 (fehlende Flash-Unterstützung)
- MMM01
- Pocket Cam
- TAMA5
- HuC-1
- HuC-3
- TAMA5 (fehlende RTC-Unterstützung)
- HuC-1 (fehlende Infrarot-Unterstützung)
- HuC-3 (fehlende RTC- und Infrarot-Unterstützung)
### Geplante Features
- Unterstützung für Link-Kabel-Multiplayer über ein Netzwerk.
- Unterstützung für Link-Kabel über Dolphin/JOY-Bus.
- M4A-Audio-Abmischung für höhere Audio-Qualität als echte Hardware.
- MP2k-Audio-Abmischung für höhere Audio-Qualität als echte Hardware.
- Unterstützung für Tool-Assisted Speedruns.
- Lua-Unterstützung für Scripting.
- Eine umfangreiche Debugging-Suite.
@ -75,10 +78,11 @@ Unterstützte Plattformen
------------------------
- Windows Vista oder neuer
- OS X 10.7 (Lion)[<sup>[3]</sup>](#osxver) oder neuer
- OS X 10.8 (Mountain Lion)[<sup>[3]</sup>](#osxver) oder neuer
- Linux
- FreeBSD
- Nintendo 3DS
- Nintendo Switch
- Wii
- PlayStation Vita
@ -112,7 +116,7 @@ Um mGBA kompilieren zu können, wird CMake 3.1 oder neuer benötigt. GCC und Cla
#### Kompilieren mit Docker
Der empfohlene Weg, um mGBA für die meisten Plattformen zu kompilieren, ist Docker. Mehrere Docker-Images sind verfügbar, welche die benötigte Compiler-Umgebung und alle benötigten Abhängigkeiten beinhaltet, um mGBA für verschiedene Plattformen zu bauen.
Der empfohlene Weg, um mGBA für die meisten Plattformen zu kompilieren, ist die Verwendung von Docker. Mehrere Docker-Images sind verfügbar, welche die benötigte Compiler-Umgebung und alle benötigten Abhängigkeiten beinhaltet, um mGBA für verschiedene Plattformen zu bauen.
Um ein Docker-Image zum Bau von mGBA zu verwenden, führe einfach folgenden Befehl in dem Verzeichnis aus, in welches Du den mGBA-Quellcode ausgecheckt hast:
@ -150,12 +154,14 @@ Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen,
brew install cmake ffmpeg libzip qt5 sdl2 libedit pkg-config
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' ..
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
make
Bitte beachte, dass Du unter macOS nicht 'make install' verwenden solltest, da dies nicht korrekt funktionieren wird.
Bitte beachte, dass Du unter macOS nicht `make install` verwenden solltest, da dies nicht korrekt funktionieren wird.
### Für Entwickler: Kompilieren unter Windows
#### Für Entwickler: Kompilieren unter Windows
##### MSYS2
Um mGBA auf Windows zu kompilieren, wird MSYS2 empfohlen. Befolge die Installationsschritte auf der [MSYS2-Website](https://msys2.github.io). Stelle sicher, dass Du die 32-Bit-Version ("MSYS2 MinGW 32-bit") (oder die 64-Bit-Version "MSYS2 MinGW 64-bit", wenn Du mGBA für x86_64 kompilieren willst) verwendest und führe folgendes Kommando (einschließlich der Klammern) aus, um alle benötigten Abhängigkeiten zu installieren. Bitte beachte, dass dafür über 1100MiB an Paketen heruntergeladen werden, was eine Weile dauern kann:
@ -181,6 +187,20 @@ Abschließend wird mGBA über folgende Kommandos kompiliert:
Bitte beachte, dass mGBA für Windows aufgrund der Vielzahl an benötigten DLLs nicht für die weitere Verteilung geeignet ist, wenn es auf diese Weise gebaut wurde. Es ist jedoch perfekt für Entwickler geeignet. Soll mGBA dennoch weiter verteilt werden (beispielsweise zu Testzwecken auf Systemen, auf denen keine MSYS2-Umgebung installiert ist), kann mithilfe des Befehls `cpack -G ZIP` ein ZIP-Archiv mit allen benötigten DLLs erstellt werden.
##### Visual Studio
mGBA mit Visual Studio zu bauen erfordert ein ähnlich kompliziertes Setup. Zuerst musst Du [vcpkg](https://github.com/Microsoft/vcpkg) installieren. Nachdem vcpkg installiert ist, musst Du noch folgende zusätzlichen Pakete installieren:
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3
Bitte beachte, dass diese Installation keine hardwarebeschleunigtes Video-Encoding auf Nvidia-Hardware unterstützen wird. Wenn Du darauf Wert legst, musst Du zuerst CUDA installieren und anschließend den vorherigen Befehl um `ffmpeg[vpx,x264,nvcodec]` ergänzen.
Zusätzlich wirst Du auch Qt installieren müssen. Unglücklicherweise steht für Qt kein Offline-Installationsprogramm für die jeweils aktuelle Version bereit. Daher musst Du entweder auf eine [ältere Version](https://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe) zurückgreifen (hierfür benötigst Du ein ansonsten nutzloses Benutzerkonto, aber Du kannst das umgehen, indem Du temporär einen ungültigen Netzwerk-Proxy hinterlegst oder über andere Methoden deine Netzwerkverbindung deaktivierst). Alternativ kannst Du auch den Online-Installer nutzen (für den ohnehin ein Benutzeraccount erfortderlich ist) oder Qt selbst mithilfe von vcpkg bauen (was verhältnismäßig lange dauert). Keine dieser Optionen ist besonders elegant. Bitte achte bei der Verwendung eines Installers darauf, die passende MSVC-Version zu wählen. Der Offline-Installer unterstützt aktuell noch nicht MSVC 2019. Die Installation mit vcpkg dauert ein wenig länger, besonders, wenn Du einen Computer mit vier oder weniger CPU-Cores nutzt:
vcpkg install qt5-base qt5-multimedia
Öffne anschließend Visual Studio, wähle "Clone Repository" und gib dort `https://github.com/mgba-emu/mgba.git` ein. Wenn Visual Studio das Repository geklont hat, gehe zu "Datei > CMake" und öffne die Datei CMakeLists.txt im Stammverzeichnis des ausgecheckten Repos. Anschließend kann mGBA in Visual Studio entwickelt werden, ähnlich wie andere Visual Studio CMake-Projekte.
#### Kompilieren mithilfe einer Toolchain
Wenn Du devkitARM (für 3DS), devkitPPC (für Wii), devkitA64 (für Switch) oder vitasdk (für PS Vita) installiert hast, kannst Du die folgenden Befehle zum Kompilieren verwenden:
@ -207,7 +227,6 @@ mGBA hat keine "harten" Abhängigkeiten. Dennoch werden die folgenden optionalen
- libedit: Für die Unterstützung des Kommandozeilen-Debuggers.
- ffmpeg oder libav: Für Videoaufzeichnungen.
- libzip oder zlib: Um ROMs aus ZIP-Dateien zu laden.
- ImageMagick: Für GIF-Aufzeichnungen.
- SQLite3: Für Spiele-Datenbanken.
- libelf: Für das Laden von ELF-Dateien.
@ -222,7 +241,7 @@ Fußnoten
<a name="flashdetect">[2]</a> In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden.
<a name="osxver">[3]</a> 10.7 wird nur für die Qt-Portierung benötigt. Die SDL-Portierung ist dafür bekannt, mit 10.5 und möglicherweise auf älteren Versionen zu funktionieren.
<a name="osxver">[3]</a> 10.8 wird nur für die Qt-Portierung benötigt. Es ist wahrscheinlich möglich, die Qt-Portierung unter macOS 10.7 und älter zu bauen und zu nutzen, aber das wird nicht offiziell unterstützt. Die SDL-Portierung ist dafür bekannt, mit 10.7 und möglicherweise auf älteren Versionen zu funktionieren.
[downloads]: http://mgba.io/downloads.html
[source]: https://github.com/mgba-emu/mgba/
@ -234,7 +253,7 @@ Copyright für mGBA © 2013 2020 Jeffrey Pfau. mGBA wird unter der [Mozilla
mGBA beinhaltet die folgenden Bibliotheken von Drittanbietern:
- [inih](https://github.com/benhoyt/inih), Copyright © 2009 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz.
- [inih](https://github.com/benhoyt/inih), Copyright © 2009 - 2020 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz.
- [blip-buf](https://code.google.com/archive/b/blip-buf), Copyright © 2003 - 2009 Shay Green, verwendet unter einer Lesser GNU Public License.
- [LZMA SDK](http://www.7-zip.org/sdk.html), Public Domain.
- [MurmurHash3](https://github.com/aappleby/smhasher), Implementierung von Austin Appleby, Public Domain.

View File

@ -129,31 +129,32 @@ typedef intptr_t ssize_t;
#define LOAD_32BE(DEST, ADDR, ARR) DEST = *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
#if defined(__PPC__) || defined(__POWERPC__)
#define LOAD_32LE(DEST, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
const void* _ptr = (ARR); \
__asm__("lwbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
}
#define LOAD_16LE(DEST, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
const void* _ptr = (ARR); \
__asm__("lhbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
}
#define STORE_32LE(SRC, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
void* _ptr = (ARR); \
__asm__("stwbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
}
#define STORE_16LE(SRC, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
void* _ptr = (ARR); \
__asm__("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
}
#ifndef _ARCH_PWR7
#define LOAD_64LE(DEST, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
union { \
struct { \
uint32_t hi; \
@ -170,7 +171,7 @@ typedef intptr_t ssize_t;
}
#define STORE_64LE(SRC, ADDR, ARR) { \
uint32_t _addr = (ADDR); \
size_t _addr = (ADDR); \
union { \
struct { \
uint32_t hi; \
@ -184,6 +185,19 @@ typedef intptr_t ssize_t;
"stwbrx %1, %2, %4 \n" \
: : "r"(bswap.hi), "r"(bswap.lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4) : "memory"); \
}
#else
#define LOAD_64LE(DEST, ADDR, ARR) { \
size_t _addr = (ADDR); \
const void* _ptr = (ARR); \
__asm__("ldbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
}
#define STORE_64LE(SRC, ADDR, ARR) { \
size_t _addr = (ADDR); \
void* _ptr = (ARR); \
__asm__("stdbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
}
#endif
#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3])

View File

@ -53,6 +53,8 @@ enum GUIIcon {
GUI_ICON_BUTTON_TRIANGLE,
GUI_ICON_BUTTON_SQUARE,
GUI_ICON_BUTTON_HOME,
GUI_ICON_STATUS_FAST_FORWARD,
GUI_ICON_STATUS_MUTE,
GUI_ICON_MAX,
};

View File

@ -69,12 +69,14 @@ static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context)
if (!entry || !thread) {
return 1;
}
*thread = threadCreate(entry, context, 0x8000, 0x18, 2, true);
*thread = threadCreate(entry, context, 0x8000, 0x18, 2, false);
return !*thread;
}
static inline int ThreadJoin(Thread* thread) {
return threadJoin(*thread, U64_MAX);
Result res = threadJoin(*thread, U64_MAX);
threadFree(*thread);
return res;
}
static inline void ThreadSetName(const char* name) {

View File

@ -64,6 +64,10 @@ extern u32* SOCUBuffer;
#ifdef __SWITCH__
#include <switch.h>
#endif
#ifdef PSP2
#include <psp2/net/net.h>
#include <psp2/sysmodule.h>
#endif
static inline void SocketSubsystemInit() {
#ifdef _WIN32
@ -78,6 +82,10 @@ static inline void SocketSubsystemInit() {
socketInitializeDefault();
#elif defined(GEKKO)
net_init();
#elif defined(PSP2)
static uint8_t netMem[1024*1024];
sceSysmoduleLoadModule(SCE_SYSMODULE_NET);
sceNetInit(&(SceNetInitParam) { netMem, sizeof(netMem) });
#endif
}
@ -92,6 +100,9 @@ static inline void SocketSubsystemDeinit() {
socketExit();
#elif defined(GEKKO)
net_deinit();
#elif defined(PSP2)
sceNetTerm();
sceSysmoduleUnloadModule(SCE_SYSMODULE_NET);
#endif
}
@ -249,6 +260,11 @@ static inline Socket SocketListen(Socket socket, int queueLength) {
#ifdef GEKKO
return net_listen(socket, queueLength);
#else
#ifdef PSP2
if (queueLength <= 0) {
queueLength = 1;
}
#endif
return listen(socket, queueLength);
#endif
}

View File

@ -17,9 +17,10 @@ struct Table {
size_t tableSize;
size_t size;
void (*deinitializer)(void*);
uint32_t seed;
};
void TableInit(struct Table*, size_t initialSize, void (deinitializer(void*)));
void TableInit(struct Table*, size_t initialSize, void (*deinitializer)(void*));
void TableDeinit(struct Table*);
void* TableLookup(const struct Table*, uint32_t key);
@ -28,10 +29,10 @@ void TableInsert(struct Table*, uint32_t key, void* value);
void TableRemove(struct Table*, uint32_t key);
void TableClear(struct Table*);
void TableEnumerate(const struct Table*, void (handler(uint32_t key, void* value, void* user)), void* user);
void TableEnumerate(const struct Table*, void (*handler)(uint32_t key, void* value, void* user), void* user);
size_t TableSize(const struct Table*);
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*)));
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*));
void HashTableDeinit(struct Table* table);
void* HashTableLookup(const struct Table*, const char* key);
@ -43,8 +44,9 @@ void HashTableRemove(struct Table*, const char* key);
void HashTableRemoveBinary(struct Table*, const void* key, size_t keylen);
void HashTableClear(struct Table*);
void HashTableEnumerate(const struct Table*, void (handler(const char* key, void* value, void* user)), void* user);
const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user);
void HashTableEnumerate(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
void HashTableEnumerateBinary(const struct Table*, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user);
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user);
const char* HashTableSearchPointer(const struct Table* table, const void* value);
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
const char* HashTableSearchString(const struct Table* table, const char* value);

View File

@ -47,7 +47,7 @@ struct VFile {
void (*unmap)(struct VFile* vf, void* memory, size_t size);
void (*truncate)(struct VFile* vf, size_t size);
ssize_t (*size)(struct VFile* vf);
bool (*sync)(struct VFile* vf, const void* buffer, size_t size);
bool (*sync)(struct VFile* vf, void* buffer, size_t size);
};
struct VDirEntry {

View File

@ -36,7 +36,7 @@ struct mStateExtdata {
struct mStateExtdataItem data[EXTDATA_MAX];
};
bool mStateExtdataInit(struct mStateExtdata*);
void mStateExtdataInit(struct mStateExtdata*);
void mStateExtdataDeinit(struct mStateExtdata*);
void mStateExtdataPut(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
bool mStateExtdataGet(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
@ -49,6 +49,7 @@ struct mCore;
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags);
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags);
void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
CXX_GUARD_END

View File

@ -38,6 +38,8 @@ enum GBMemoryBankControllerType {
GB_MBC5_RUMBLE = 0x105,
GB_UNL_WISDOM_TREE = 0x200,
GB_UNL_PKJD = 0x203,
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
GB_UNL_HITEK = 0x221,
};
struct GBSIODriver {

View File

@ -150,6 +150,11 @@ struct GBPKJDState {
uint8_t reg[2];
};
struct GBBBDState {
int dataSwapMode;
int bankSwapMode;
};
union GBMBCState {
struct GBMBC1State mbc1;
struct GBMBC6State mbc6;
@ -158,6 +163,7 @@ union GBMBCState {
struct GBPocketCamState pocketCam;
struct GBTAMA5State tama5;
struct GBPKJDState pkjd;
struct GBBBDState bbd;
};
struct mRotationSource;

View File

@ -391,6 +391,10 @@ struct GBSerializedState {
uint8_t locked;
uint8_t bank0;
} mmm01;
struct {
uint8_t dataSwapMode;
uint8_t bankSwapMode;
} bbd;
struct {
uint8_t reserved[16];
} padding;

View File

@ -15,6 +15,8 @@ CXX_GUARD_START
#include <mgba/internal/gb/audio.h>
#include <mgba-util/circle-buffer.h>
#define GBA_AUDIO_FIFO_SIZE 8
#define MP2K_MAGIC 0x68736D53
#define MP2K_MAX_SOUND_CHANNELS 12
@ -26,7 +28,11 @@ extern const unsigned GBA_AUDIO_SAMPLES;
extern const int GBA_AUDIO_VOLUME_MAX;
struct GBAAudioFIFO {
struct CircleBuffer fifo;
uint32_t fifo[GBA_AUDIO_FIFO_SIZE];
int fifoWrite;
int fifoRead;
uint32_t internalSample;
int internalRemaining;
int dmaSource;
int8_t sample;
};
@ -298,7 +304,7 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value);
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value);
void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
struct GBASerializedState;

View File

@ -20,7 +20,7 @@ extern const uint32_t GBA_SAVESTATE_VERSION;
mLOG_DECLARE_CATEGORY(GBA_STATE);
/* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000001)
* 0x00000 - 0x00003: Version Magic (0x01000004)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Master cycles
@ -69,10 +69,16 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x0018C - 0x001AB: Audio FIFO 1
* 0x001AC - 0x001CB: Audio FIFO 2
* 0x001CC - 0x001DF: Audio miscellaneous state
* | 0x001CC - 0x001CF: FIFO 1 size
* | 0x001D0 - 0x001D3: Reserved
* | 0x001CC - 0x001CF: Channel A internal audio samples
* | 0x001D0 - 0x001D3: Channel B internal audio samples
* | 0x001D4 - 0x001D7: Next sample
* | 0x001D8 - 0x001DB: FIFO 2 size
* | 0x001D8: Channel A current sample
* | 0x001D9: Channel B current sample
* | 0x001DA - 0x001DB: Flags
* | bits 0 - 1: Channel B internal samples remaining
* | bits 2 - 4: Channel B readable words
* | bits 5 - 6: Channel A internal samples remaining
* | bits 7 - 9: Channel A readable words
* | TODO: Fix this, they're in big-endian order, but field is little-endian
* | 0x001DC - 0x001DC: Channel 1 envelope state
* | bits 0 - 3: Current volume
@ -217,6 +223,12 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* Total size: 0x61000 (397,312) bytes
*/
DECL_BITFIELD(GBASerializedAudioFlags, uint16_t);
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesB, 0, 2);
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy?
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2);
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3);
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
@ -267,12 +279,14 @@ struct GBASerializedState {
struct {
struct GBSerializedPSGState psg;
uint8_t fifoA[32];
uint8_t fifoB[32];
uint32_t fifoSizeA;
int32_t reserved;
uint32_t fifoA[8];
uint32_t fifoB[8];
uint32_t internalA;
uint32_t internalB;
int32_t nextSample;
uint32_t fifoSizeB;
int8_t sampleA;
int8_t sampleB;
GBASerializedAudioFlags gbaFlags;
GBSerializedAudioFlags flags;
} audio;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -400,9 +400,11 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
if (block && bsize >= phdr->p_filesz && esize > phdr->p_offset && esize >= phdr->p_filesz + phdr->p_offset) {
memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz);
} else {
ELFProgramHeadersDeinit(&ph);
return false;
}
}
ELFProgramHeadersDeinit(&ph);
return true;
}

View File

@ -31,9 +31,8 @@ struct mStateExtdataHeader {
int64_t offset;
};
bool mStateExtdataInit(struct mStateExtdata* extdata) {
void mStateExtdataInit(struct mStateExtdata* extdata) {
memset(extdata->data, 0, sizeof(extdata->data));
return true;
}
void mStateExtdataDeinit(struct mStateExtdata* extdata) {
@ -43,6 +42,7 @@ void mStateExtdataDeinit(struct mStateExtdata* extdata) {
extdata->data[i].clean(extdata->data[i].data);
}
}
memset(extdata->data, 0, sizeof(extdata->data));
}
void mStateExtdataPut(struct mStateExtdata* extdata, enum mStateExtdataTag tag, struct mStateExtdataItem* item) {
@ -215,7 +215,7 @@ static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
if (!strcmp((const char*) chunk->name, "gbAs")) {
void* state = bundle->state;
if (!state) {
return 0;
return 1;
}
uLongf len = bundle->stateSize;
uncompress((Bytef*) state, &len, chunk->data, chunk->size);
@ -296,6 +296,54 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
}
return state;
}
static bool _loadPNGExtadata(struct VFile* vf, struct mStateExtdata* extdata) {
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (!png || !info || !end) {
PNGReadClose(png, info, end);
return false;
}
struct mBundledState bundle = {
.stateSize = 0,
.state = NULL,
.extdata = extdata
};
PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx");
bool success = PNGReadHeader(png, info);
if (!success) {
PNGReadClose(png, info, end);
return false;
}
unsigned width = png_get_image_width(png, info);
unsigned height = png_get_image_height(png, info);
uint32_t* pixels = NULL;
pixels = malloc(width * height * 4);
if (!pixels) {
PNGReadClose(png, info, end);
return false;
}
success = PNGReadPixels(png, info, pixels, width, height, width);
success = success && PNGReadFooter(png, end);
PNGReadClose(png, info, end);
if (success) {
struct mStateExtdataItem item = {
.size = width * height * 4,
.data = pixels,
.clean = free
};
mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item);
} else {
free(pixels);
return false;
}
return true;
}
#endif
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
@ -423,6 +471,20 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
return state;
}
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata) {
#ifdef USE_PNG
if (isPNG(vf)) {
return _loadPNGExtadata(vf, extdata);
}
#endif
if (!core) {
return false;
}
ssize_t stateSize = core->stateSize(core);
vf->seek(vf, stateSize, SEEK_SET);
return mStateExtdataDeserialize(extdata, vf);
}
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
struct mStateExtdata extdata;
mStateExtdataInit(&extdata);

View File

@ -61,6 +61,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Show status OSD",
.data = "showOSD",
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
"Off", "On"
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Autosave state",
.data = "autosave",
@ -81,6 +91,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Mute",
.data = "mute",
.submenu = 0,
.state = false,
.validStates = (const char*[]) {
"Off", "On"
},
.nStates = 2
};
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Use BIOS if found",
.data = "useBios",
@ -91,10 +111,12 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
},
.nStates = 2
};
#ifdef M_CORE_GBA
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select GBA BIOS path",
.data = "gba.bios",
};
#endif
#ifdef M_CORE_GB
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Select GB BIOS path",

View File

@ -56,6 +56,7 @@ static const struct mInputPlatformInfo _mGUIKeyInfo = {
[mGUI_INPUT_SCREENSHOT] = "Take screenshot",
[mGUI_INPUT_FAST_FORWARD_HELD] = "Fast forward (held)",
[mGUI_INPUT_FAST_FORWARD_TOGGLE] = "Fast forward (toggle)",
[mGUI_INPUT_MUTE_TOGGLE] = "Mute (toggle)",
},
.nKeys = GUI_INPUT_MAX
};
@ -201,6 +202,7 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) {
#else
mCoreConfigSetDefaultIntValue(&runner->config, "autosave", true);
#endif
mCoreConfigSetDefaultIntValue(&runner->config, "showOSD", true);
mCoreConfigLoad(&runner->config);
mCoreConfigGetIntValue(&runner->config, "logLevel", &logger.logLevel);
@ -429,6 +431,12 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
mCoreLoadState(runner->core, 0, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
}
int showOSD = true;
mCoreConfigGetIntValue(&runner->config, "showOSD", &showOSD);
int drawFps = false;
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
bool running = true;
#ifndef DISABLE_THREADING
@ -490,6 +498,11 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->setFrameLimiter(runner, true);
}
}
if (guiKeys & (1 << mGUI_INPUT_MUTE_TOGGLE)) {
int mute = !runner->core->opts.mute;
mCoreConfigSetUIntValue(&runner->config, "mute", mute);
runner->core->reloadConfigOption(runner->core, "mute", &runner->config);
}
uint16_t keys = runner->pollGameInput(runner);
if (runner->prepareForFrame) {
runner->prepareForFrame(runner);
@ -497,16 +510,29 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->core->setKeys(runner->core, keys);
runner->core->runFrame(runner->core);
if (runner->drawFrame) {
int drawFps = false;
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
runner->params.drawStart();
runner->drawFrame(runner, false);
if (drawFps) {
if (showOSD || drawFps) {
if (runner->params.guiPrepare) {
runner->params.guiPrepare();
}
GUIFontPrintf(runner->params.font, 0, GUIFontHeight(runner->params.font), GUI_ALIGN_LEFT, 0x7FFFFFFF, "%.2f fps", runner->fps);
if (drawFps) {
GUIFontPrintf(runner->params.font, 0, GUIFontHeight(runner->params.font), GUI_ALIGN_LEFT, 0x7FFFFFFF, "%.2f fps", runner->fps);
}
if (showOSD) {
unsigned origin = runner->params.width - GUIFontHeight(runner->params.font) / 2;
unsigned w;
if (fastForward || (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD_HELD))) {
GUIFontDrawIcon(runner->params.font, origin, GUIFontHeight(runner->params.font) / 2, GUI_ALIGN_RIGHT, 0, 0x7FFFFFFF, GUI_ICON_STATUS_FAST_FORWARD);
GUIFontIconMetrics(runner->params.font, GUI_ICON_STATUS_FAST_FORWARD, &w, NULL);
origin -= w + GUIFontHeight(runner->params.font) / 2;
}
if (runner->core->opts.mute) {
GUIFontDrawIcon(runner->params.font, origin, GUIFontHeight(runner->params.font) / 2, GUI_ALIGN_RIGHT, 0, 0x7FFFFFFF, GUI_ICON_STATUS_MUTE);
GUIFontIconMetrics(runner->params.font, GUI_ICON_STATUS_MUTE, &w, NULL);
origin -= w + GUIFontHeight(runner->params.font) / 2;
}
}
if (runner->params.guiFinish) {
runner->params.guiFinish();
}
@ -597,6 +623,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
if (runner->unpaused) {
runner->unpaused(runner);
}
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
mCoreConfigGetIntValue(&runner->config, "showOSD", &showOSD);
}
mLOG(GUI_RUNNER, DEBUG, "Shutting down...");
if (runner->gameUnloaded) {

View File

@ -23,7 +23,8 @@ enum mGUIInput {
mGUI_INPUT_SCREEN_MODE,
mGUI_INPUT_SCREENSHOT,
mGUI_INPUT_FAST_FORWARD_HELD,
mGUI_INPUT_FAST_FORWARD_TOGGLE
mGUI_INPUT_FAST_FORWARD_TOGGLE,
mGUI_INPUT_MUTE_TOGGLE,
};
struct mGUIBackground {

View File

@ -206,10 +206,6 @@ static THREAD_ENTRY _proxyThread(void* logger) {
}
}
MutexUnlock(&proxyRenderer->mutex);
#ifdef _3DS
svcExitThread();
#endif
return 0;
}

View File

@ -296,11 +296,16 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) {
mVideoLoggerRendererRunInjected(logger);
ignorePackets = channel->ignorePackets;
}
struct mVideoLoggerDirtyInfo buffer = {0};
struct mVideoLoggerDirtyInfo item = {0};
while (logger->readData(logger, &item, sizeof(item), block)) {
while (logger->readData(logger, &buffer, sizeof(buffer), block)) {
LOAD_32LE(item.type, 0, &buffer.type);
if (ignorePackets & (1 << item.type)) {
continue;
}
LOAD_32LE(item.address, 0, &buffer.address);
LOAD_32LE(item.value, 0, &buffer.value);
LOAD_32LE(item.value2, 0, &buffer.value2);
switch (item.type) {
case DIRTY_SCANLINE:
if (channel && channel->injectionPoint == LOGGER_INJECTION_FIRST_SCANLINE && !channel->injecting && item.address == 0) {

View File

@ -138,8 +138,10 @@ static void _GBCoreDeinit(struct mCore* core) {
GBDestroy(core->board);
mappedMemoryFree(core->cpu, sizeof(struct SM83Core));
mappedMemoryFree(core->board, sizeof(struct GB));
#if defined USE_DEBUGGERS && (!defined(MINIMAL_CORE) || MINIMAL_CORE < 2)
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
mDirectorySetDeinit(&core->dirs);
#endif
#ifdef USE_DEBUGGERS
if (core->symbolTable) {
mDebuggerSymbolTableDestroy(core->symbolTable);
}

View File

@ -700,7 +700,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->memory.ie = state->ie;
gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52);
if (GBAudioEnableGetEnable(gb->audio.enable)) {
if (gb->audio.enable) {
GBIOWrite(gb, REG_NR10, gb->memory.io[REG_NR10]);
GBIOWrite(gb, REG_NR11, gb->memory.io[REG_NR11]);
GBIOWrite(gb, REG_NR12, gb->memory.io[REG_NR12]);

View File

@ -37,6 +37,8 @@ static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
@ -45,6 +47,8 @@ static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t valu
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
static void _GBPocketCamCapture(struct GBMemory*);
@ -145,6 +149,31 @@ static bool _isWisdomTree(const uint8_t* mem, size_t size) {
return false;
}
static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t size) {
const struct GBCartridge* cart = (const struct GBCartridge*) &mem[0x100];
switch (cart->type) {
case 0:
if (_isWisdomTree(mem, size)) {
return GB_UNL_WISDOM_TREE;
}
break;
}
uint32_t secondaryLogo = doCrc32(&mem[0x184], 0x30);
switch (secondaryLogo) {
case 0x4fdab691:
return GB_UNL_HITEK;
case 0xc7d8c1df:
case 0x6d1ea662: // Garou
if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version
return GB_UNL_BBD;
}
}
return GB_MBC_AUTODETECT;
}
void GBMBCSwitchSramBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) {
@ -202,15 +231,13 @@ void GBMBCInit(struct GB* gb) {
gb->sramSize = 0x10000;
break;
}
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
gb->memory.mbcType = _detectUnlMBC(gb->memory.rom, gb->memory.romSize);
}
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
switch (cart->type) {
case 0:
if (_isWisdomTree(gb->memory.rom, gb->memory.romSize)) {
gb->memory.mbcType = GB_UNL_WISDOM_TREE;
break;
}
// Fall through
case 8:
case 9:
gb->memory.mbcType = GB_MBC_NONE;
@ -346,6 +373,16 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_WISDOM_TREE:
gb->memory.mbcWrite = _GBWisdomTree;
break;
case GB_UNL_BBD:
gb->memory.mbcWrite = _GBBBD;
gb->memory.mbcRead = _GBBBDRead;
break;
case GB_UNL_HITEK:
gb->memory.mbcWrite = _GBHitek;
gb->memory.mbcRead = _GBHitekRead;
gb->memory.mbcState.bbd.dataSwapMode = 7;
gb->memory.mbcState.bbd.bankSwapMode = 7;
break;
case GB_UNL_PKJD:
gb->memory.mbcWrite = _GBPKJD;
gb->memory.mbcRead = _GBPKJDRead;
@ -1306,6 +1343,123 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
}
}
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
uint8_t newbyte = 0;
int i;
for(i = 0; i < 8; ++i) {
int oldbit = reorder[i];
int newbit = i;
newbyte += ((input >> oldbit) & 1) << newbit;
}
return newbyte;
}
static const uint8_t _bbdDataReordering[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
{ 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
{ 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
{ 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
};
static const uint8_t _bbdBankReordering[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
{ 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
{ 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
};
void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
switch (address & 0xF0FF) {
case 0x2000:
value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
break;
case 0x2001:
memory->mbcState.bbd.dataSwapMode = value & 0x07;
if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
}
break;
case 0x2080:
memory->mbcState.bbd.bankSwapMode = value & 0x07;
if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
}
break;
}
_GBMBC5(gb, address, value);
}
uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
switch (address >> 14) {
case 0:
default:
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
case 1:
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
}
}
static const uint8_t _hitekDataReordering[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 0, 6, 5, 3, 4, 1, 2, 7 },
{ 0, 5, 6, 3, 4, 2, 1, 7 },
{ 0, 6, 2, 3, 4, 5, 1, 7 },
{ 0, 6, 1, 3, 4, 5, 2, 7 },
{ 0, 1, 6, 3, 4, 5, 2, 7 },
{ 0, 2, 6, 3, 4, 1, 5, 7 },
{ 0, 6, 2, 3, 4, 1, 5, 7 },
};
static const uint8_t _hitekBankReordering[8][8] = {
{ 0, 1, 2, 3, 4, 5, 6, 7 },
{ 3, 2, 1, 0, 4, 5, 6, 7 },
{ 2, 1, 0, 3, 4, 5, 6, 7 },
{ 1, 0, 3, 2, 4, 5, 6, 7 },
{ 0, 3, 2, 1, 4, 5, 6, 7 },
{ 2, 3, 0, 1, 4, 5, 6, 7 },
{ 3, 0, 1, 2, 4, 5, 6, 7 },
{ 2, 0, 3, 1, 4, 5, 6, 7 },
};
void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
switch (address & 0xF0FF) {
case 0x2000:
value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
break;
case 0x2001:
memory->mbcState.bbd.dataSwapMode = value & 0x07;
break;
case 0x2080:
memory->mbcState.bbd.bankSwapMode = value & 0x07;
break;
case 0x300:
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
return;
}
_GBMBC5(gb, address, value);
}
uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
switch (address >> 14) {
case 0:
default:
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
case 1:
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
}
}
void GBMBCRTCRead(struct GB* gb) {
struct GBMBCRTCSaveBuffer rtcBuffer;
struct VFile* vf = gb->sramVf;

View File

@ -84,6 +84,10 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
case GB_REGION_CART_BANK1 + 1:
case GB_REGION_CART_BANK1 + 2:
case GB_REGION_CART_BANK1 + 3:
if ((gb->memory.mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
cpu->memory.cpuLoad8 = GBLoad8;
break;
}
cpu->memory.cpuLoad8 = GBFastLoad8;
if (gb->memory.mbcType != GB_MBC6) {
cpu->memory.activeRegion = memory->romBank;
@ -277,6 +281,9 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
if (address >= memory->romSize) {
return 0xFF;
}
if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
return memory->mbcRead(memory, address);
}
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
case GB_REGION_VRAM:
case GB_REGION_VRAM + 1:
@ -754,6 +761,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode;
break;
default:
break;
}
@ -841,6 +853,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
}
break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7;
break;
default:
break;
}

View File

@ -23,7 +23,6 @@
mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio");
const unsigned GBA_AUDIO_SAMPLES = 2048;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
const int GBA_AUDIO_VOLUME_MAX = 0x100;
static const int CLOCKS_PER_FRAME = 0x800;
@ -48,8 +47,6 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
// Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
audio->externalMixing = false;
audio->forceDisableChA = false;
@ -64,7 +61,17 @@ void GBAAudioReset(struct GBAAudio* audio) {
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
audio->chA.dmaSource = 1;
audio->chB.dmaSource = 2;
audio->chA.fifoWrite = 0;
audio->chA.fifoRead = 0;
audio->chA.internalSample = 0;
audio->chA.internalRemaining = 0;
memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo));
audio->chA.sample = 0;
audio->chB.fifoWrite = 0;
audio->chB.fifoRead = 0;
audio->chB.internalSample = 0;
audio->chB.internalRemaining = 0;
memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo));
audio->chB.sample = 0;
audio->sampleRate = 0x8000;
audio->soundbias = 0x200;
@ -84,14 +91,10 @@ void GBAAudioReset(struct GBAAudio* audio) {
blip_clear(audio->psg.left);
blip_clear(audio->psg.right);
audio->clock = 0;
CircleBufferClear(&audio->chA.fifo);
CircleBufferClear(&audio->chB.fifo);
}
void GBAAudioDeinit(struct GBAAudio* audio) {
GBAudioDeinit(&audio->psg);
CircleBufferDeinit(&audio->chA.fifo);
CircleBufferDeinit(&audio->chB.fifo);
}
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
@ -199,10 +202,12 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value);
audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value);
if (GBARegisterSOUNDCNT_HIIsChAReset(value)) {
CircleBufferClear(&audio->chA.fifo);
audio->chA.fifoWrite = 0;
audio->chA.fifoRead = 0;
}
if (GBARegisterSOUNDCNT_HIIsChBReset(value)) {
CircleBufferClear(&audio->chB.fifo);
audio->chB.fifoWrite = 0;
audio->chB.fifoRead = 0;
}
}
@ -219,26 +224,25 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
audio->psg.ch3.wavedata32[address | (!audio->psg.ch3.bank * 4)] = value;
}
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
struct CircleBuffer* fifo;
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
struct GBAAudioFIFO* channel;
switch (address) {
case REG_FIFO_A_LO:
fifo = &audio->chA.fifo;
channel = &audio->chA;
break;
case REG_FIFO_B_LO:
fifo = &audio->chB.fifo;
channel = &audio->chB;
break;
default:
mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", address);
return;
return value;
}
int i;
for (i = 0; i < 4; ++i) {
while (!CircleBufferWrite8(fifo, value >> (8 * i))) {
int8_t dummy;
CircleBufferRead8(fifo, &dummy);
}
channel->fifo[channel->fifoWrite] = value;
++channel->fifoWrite;
if (channel->fifoWrite == GBA_AUDIO_FIFO_SIZE) {
channel->fifoWrite = 0;
}
return channel->fifo[channel->fifoWrite];
}
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
@ -251,17 +255,33 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", fifoId);
return;
}
if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) {
int fifoSize;
if (channel->fifoWrite >= channel->fifoRead) {
fifoSize = channel->fifoWrite - channel->fifoRead;
} else {
fifoSize = GBA_AUDIO_FIFO_SIZE - channel->fifoRead + channel->fifoWrite;
}
if (GBA_AUDIO_FIFO_SIZE - fifoSize > 4 && channel->dmaSource > 0) {
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
dma->nextCount = 4;
GBADMASchedule(audio->p, channel->dmaSource, dma);
} else {
channel->dmaSource = 0;
}
}
CircleBufferRead8(&channel->fifo, (int8_t*) &channel->sample);
if (!channel->internalRemaining && fifoSize) {
channel->internalSample = channel->fifo[channel->fifoRead];
channel->internalRemaining = 4;
++channel->fifoRead;
if (channel->fifoRead == GBA_AUDIO_FIFO_SIZE) {
channel->fifoRead = 0;
}
}
channel->sample = channel->internalSample;
if (channel->internalRemaining) {
channel->internalSample >>= 8;
--channel->internalRemaining;
}
}
static int _applyBias(struct GBAAudio* audio, int sample) {
@ -345,37 +365,76 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
GBAudioPSGSerialize(&audio->psg, &state->audio.psg, &state->audio.flags);
CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA));
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
STORE_32(fifoSize, 0, &state->audio.fifoSizeA);
fifoSize = CircleBufferSize(&audio->chB.fifo);
STORE_32(fifoSize, 0, &state->audio.fifoSizeB);
STORE_32(audio->chA.internalSample, 0, &state->audio.internalA);
STORE_32(audio->chB.internalSample, 0, &state->audio.internalB);
state->audio.sampleA = audio->chA.sample;
state->audio.sampleB = audio->chB.sample;
int readA = audio->chA.fifoRead;
int readB = audio->chB.fifoRead;
size_t i;
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
++readA;
if (readA == GBA_AUDIO_FIFO_SIZE) {
readA = 0;
}
++readB;
if (readB == GBA_AUDIO_FIFO_SIZE) {
readB = 0;
}
}
int fifoSizeA;
if (audio->chA.fifoWrite >= audio->chA.fifoRead) {
fifoSizeA = audio->chA.fifoWrite - audio->chA.fifoRead;
} else {
fifoSizeA = GBA_AUDIO_FIFO_SIZE - audio->chA.fifoRead + audio->chA.fifoWrite;
}
int fifoSizeB;
if (audio->chB.fifoWrite >= audio->chB.fifoRead) {
fifoSizeB = audio->chB.fifoWrite - audio->chB.fifoRead;
} else {
fifoSizeB = GBA_AUDIO_FIFO_SIZE - audio->chB.fifoRead + audio->chB.fifoWrite;
}
GBASerializedAudioFlags flags = 0;
flags = GBASerializedAudioFlagsSetFIFOSamplesA(flags, fifoSizeA);
flags = GBASerializedAudioFlagsSetFIFOSamplesB(flags, fifoSizeB);
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining);
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
STORE_16(flags, 0, &state->audio.gbaFlags);
STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
}
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
GBAudioPSGDeserialize(&audio->psg, &state->audio.psg, &state->audio.flags);
CircleBufferClear(&audio->chA.fifo);
CircleBufferClear(&audio->chB.fifo);
uint32_t fifoSize;
LOAD_32(fifoSize, 0, &state->audio.fifoSizeA);
if (fifoSize > CircleBufferCapacity(&audio->chA.fifo)) {
fifoSize = CircleBufferCapacity(&audio->chA.fifo);
}
size_t i;
for (i = 0; i < fifoSize; ++i) {
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
}
LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA);
LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB);
audio->chA.sample = state->audio.sampleA;
audio->chB.sample = state->audio.sampleB;
LOAD_32(fifoSize, 0, &state->audio.fifoSizeB);
if (fifoSize > CircleBufferCapacity(&audio->chB.fifo)) {
fifoSize = CircleBufferCapacity(&audio->chB.fifo);
}
for (i = 0; i < fifoSize; ++i) {
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]);
int readA = 0;
int readB = 0;
size_t i;
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
++readA;
++readB;
}
audio->chA.fifoRead = 0;
audio->chB.fifoRead = 0;
GBASerializedAudioFlags flags;
LOAD_16(flags, 0, &state->audio.gbaFlags);
audio->chA.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesA(flags);
audio->chB.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesB(flags);
audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags);
audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
uint32_t when;
LOAD_32(when, 0, &state->audio.nextSample);

View File

@ -228,6 +228,11 @@ static void _GBACoreDeinit(struct mCore* core) {
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
mDirectorySetDeinit(&core->dirs);
#endif
#ifdef USE_DEBUGGERS
if (core->symbolTable) {
mDebuggerSymbolTableDestroy(core->symbolTable);
}
#endif
struct GBACore* gbacore = (struct GBACore*) core;
free(gbacore->debuggerPlatform);

View File

@ -134,29 +134,37 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
struct GBAMemory* memory = &gba->memory;
struct GBADMA* dma;
bool found = false;
int i;
for (i = 0; i < 4; ++i) {
dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_HBLANK && !dma->nextCount) {
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
dma->nextCount = dma->count;
found = true;
}
}
GBADMAUpdate(gba);
if (found) {
GBADMAUpdate(gba);
}
}
void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
struct GBAMemory* memory = &gba->memory;
struct GBADMA* dma;
bool found = false;
int i;
for (i = 0; i < 4; ++i) {
dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_VBLANK && !dma->nextCount) {
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
dma->nextCount = dma->count;
found = true;
}
}
GBADMAUpdate(gba);
if (found) {
GBADMAUpdate(gba);
}
}
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles) {
@ -211,7 +219,7 @@ void GBADMAUpdate(struct GBA* gba) {
struct GBADMA* dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
int32_t time = dma->when - currentTime;
if (memory->activeDMA == -1 || (dma->count == dma->nextCount && time < leastTime)) {
if (memory->activeDMA == -1 || time < leastTime) {
leastTime = time;
memory->activeDMA = i;
}
@ -295,6 +303,16 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
info->nextCount = wordsRemaining;
info->nextSource = source;
info->nextDest = dest;
int i;
for (i = 0; i < 4; ++i) {
struct GBADMA* dma = &memory->dma[i];
int32_t time = dma->when - info->when;
if (time < 0 && GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
dma->when = info->when;
}
}
if (!wordsRemaining) {
info->nextCount |= 0x80000000;
if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {

View File

@ -289,9 +289,11 @@ void _mp2kStep(struct GBAAudioMixer* mixer) {
CircleBufferRead16(&mixer->activeTracks[track].buffer, &value);
sample.right += value;
}
sample.left = (sample.left * mixer->p->masterVolume) >> 8;
sample.right = (sample.right * mixer->p->masterVolume) >> 8;
if (mixer->p->externalMixing) {
blip_add_delta(mixer->p->psg.left, mixer->p->clock + i * interval, sample.left - mixer->last.left);
blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.left - mixer->last.left);
blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.right - mixer->last.right);
}
mixer->last = sample;
}

View File

@ -192,13 +192,13 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
break;
case DIRTY_PALETTE:
if (item->address < SIZE_PALETTE_RAM) {
logger->palette[item->address >> 1] = item->value;
STORE_16LE(item->value, item->address, logger->palette);
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
}
break;
case DIRTY_OAM:
if (item->address < SIZE_OAM) {
logger->oam[item->address] = item->value;
STORE_16LE(item->value, item->address << 1, logger->oam);
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
}
break;

View File

@ -495,15 +495,15 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
// == Game Boy Player
static const uint16_t _logoPalette[] = {
0xFFDF, 0x640C, 0xE40C, 0xE42D, 0x644E, 0xE44E, 0xE46E, 0x68AF,
0xE8B0, 0x68D0, 0x68F0, 0x6911, 0xE911, 0x6D32, 0xED32, 0xED73,
0x6D93, 0xED94, 0x6DB4, 0xF1D5, 0x71F5, 0xF1F6, 0x7216, 0x7257,
0xF657, 0x7678, 0xF678, 0xF699, 0xF6B9, 0x76D9, 0xF6DA, 0x7B1B,
0xFB1B, 0xFB3C, 0x7B5C, 0x7B7D, 0xFF7D, 0x7F9D, 0x7FBE, 0x7FFF,
0x642D, 0x648E, 0xE88F, 0xE8F1, 0x6D52, 0x6D73, 0xF1B4, 0xF216,
0x7237, 0x7698, 0x7AFA, 0xFAFA, 0xFB5C, 0xFFBE, 0x7FDE, 0xFFFF,
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
static const uint8_t _logoPalette[] = {
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
0xB0, 0xE8, 0xD0, 0x68, 0xF0, 0x68, 0x11, 0x69, 0x11, 0xE9, 0x32, 0x6D, 0x32, 0xED, 0x73, 0xED,
0x93, 0x6D, 0x94, 0xED, 0xB4, 0x6D, 0xD5, 0xF1, 0xF5, 0x71, 0xF6, 0xF1, 0x16, 0x72, 0x57, 0x72,
0x57, 0xF6, 0x78, 0x76, 0x78, 0xF6, 0x99, 0xF6, 0xB9, 0xF6, 0xD9, 0x76, 0xDA, 0xF6, 0x1B, 0x7B,
0x1B, 0xFB, 0x3C, 0xFB, 0x5C, 0x7B, 0x7D, 0x7B, 0x7D, 0xFF, 0x9D, 0x7F, 0xBE, 0x7F, 0xFF, 0x7F,
0x2D, 0x64, 0x8E, 0x64, 0x8F, 0xE8, 0xF1, 0xE8, 0x52, 0x6D, 0x73, 0x6D, 0xB4, 0xF1, 0x16, 0xF2,
0x37, 0x72, 0x98, 0x76, 0xFA, 0x7A, 0xFA, 0xFA, 0x5C, 0xFB, 0xBE, 0xFF, 0xDE, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint32_t _logoHash = 0xEEDA6963;

View File

@ -429,12 +429,12 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
case REG_FIFO_A_LO:
case REG_FIFO_B_LO:
GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value);
break;
return;
case REG_FIFO_A_HI:
case REG_FIFO_B_HI:
GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));
break;
return;
// DMA
case REG_DMA0SAD_LO:
@ -630,7 +630,7 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
break;
case REG_FIFO_A_LO:
case REG_FIFO_B_LO:
GBAAudioWriteFIFO(&gba->audio, address, value);
value = GBAAudioWriteFIFO(&gba->audio, address, value);
break;
case REG_DMA0SAD_LO:
value = GBADMAWriteSAD(gba, 0, value);

View File

@ -1636,6 +1636,10 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23;
x >>= 23;
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) >= 3 && GBAObjAttributesCGetTile(sprite->c) < 512) {
return;
}
int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt);
unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10;
int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a));

View File

@ -57,7 +57,7 @@
\
uint32_t current = renderer->row[outX]; \
MOSAIC(COORD) \
if (pixelData) { \
if (pixelData && IS_WRITABLE(current)) { \
COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
} \
}

View File

@ -47,7 +47,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
} else {
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
} else {
color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
@ -56,14 +56,11 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
}
static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
if (!IS_WRITABLE(current)) { \
return; \
} \
if (color >= current) {
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
} else {
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
} else {
color = color & ~FLAG_TARGET_2;
@ -77,7 +74,7 @@ static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* rend
if (color < current) {
color |= (current & FLAG_OBJWIN);
} else {
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
renderer->row[x] = color;
}
@ -86,15 +83,12 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
uint32_t current) {
UNUSED(renderer);
if (color >= current) {
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
}
renderer->row[x] = color;
}
#define COMPOSITE_16_OBJWIN(BLEND, IDX) \
if (!IS_WRITABLE(current)) { \
continue; \
} \
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
unsigned mergedFlags = flags; \
@ -108,9 +102,6 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
_composite ## BLEND ## NoObjwin(renderer, outX + IDX, palette[pixelData] | flags, current);
#define COMPOSITE_256_OBJWIN(BLEND, IDX) \
if (!IS_WRITABLE(current)) { \
continue; \
} \
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
unsigned mergedFlags = flags; \

View File

@ -15,7 +15,7 @@
#include <fcntl.h>
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000003;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000004;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");

View File

@ -69,7 +69,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec
return false;
}
LOAD_32(size, 0, &buffer.i);
if (size < 0x1C || size >= SIZE_CART_FLASH1M + 0x1C) {
if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
return false;
}
char* payload = malloc(size);

View File

@ -38,7 +38,7 @@ static void* _vf3dMap(struct VFile* vf, size_t size, int flags);
static void _vf3dUnmap(struct VFile* vf, void* memory, size_t size);
static void _vf3dTruncate(struct VFile* vf, size_t size);
static ssize_t _vf3dSize(struct VFile* vf);
static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vf3dSync(struct VFile* vf, void* buffer, size_t size);
static bool _vd3dClose(struct VDir* vd);
static void _vd3dRewind(struct VDir* vd);
@ -160,14 +160,12 @@ ssize_t _vf3dSize(struct VFile* vf) {
return size;
}
static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size) {
static bool _vf3dSync(struct VFile* vf, void* buffer, size_t size) {
struct VFile3DS* vf3d = (struct VFile3DS*) vf;
if (buffer) {
u32 sizeWritten;
Result res = FSFILE_Write(vf3d->handle, &sizeWritten, 0, buffer, size, FS_WRITE_FLUSH);
if (res) {
return false;
}
return R_SUCCEEDED(res);
}
FSFILE_Flush(vf3d->handle);
return true;

View File

@ -7,6 +7,7 @@ find_program(BANNERTOOL bannertool)
find_program(MAKEROM makerom)
find_program(PICASSO picasso)
find_program(RAW2C raw2c)
find_program(TEX3DS tex3ds)
set(STRIP "${cross_prefix_path}strip" CACHE INTERNAL "symbol stripper")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format" PARENT_SCOPE)
@ -65,9 +66,15 @@ add_custom_command(OUTPUT ${BINARY_NAME}.bnr
COMMAND ${BANNERTOOL} makebanner -ci ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx -a ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav -o ${BINARY_NAME}.bnr
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav)
# tex3ds binaries as of 2.0.1-3 crash if you try to do this
#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.t3x
# COMMAND ${TEX3DS} -f rgb5551 -o icons.t3x ${CMAKE_SOURCE_DIR}/res/icons.png
# MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/icons.png
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw
DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw)
COMMAND ${RAW2C} ${CMAKE_CURRENT_SOURCE_DIR}/icons.t3x
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/icons.t3x)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h

View File

@ -80,6 +80,10 @@ bool ctrInitGpu(void) {
AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
AttrInfo_AddLoader(attrInfo, 3, GPU_FLOAT, 2); // in_rot
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 4, 0x3210);
return true;
}
@ -213,13 +217,8 @@ void ctrStartFrame(void) {
ctrNumVerts = 0;
ctrVertStart = 0;
activeTexture = NULL;
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
BufInfo_Init(bufInfo);
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 4, 0x3210);
}
void ctrEndFrame(void) {
ctrFlushBatch();
GSPGPU_FlushDataCache(ctrVertexBuffer, sizeof(struct ctrUIVertex) * ctrNumVerts);
}

View File

@ -9,6 +9,7 @@
#include <mgba-util/vfs.h>
#include "icons.h"
#include <tex3ds.h>
#include "ctr-gpu.h"
#define FONT_SIZE 15.6f
@ -44,12 +45,8 @@ struct GUIFont* GUIFontCreate(void) {
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
}
tex = &guiFont->icons;
C3D_TexInitVRAM(tex, 256, 64, GPU_RGBA5551);
GSPGPU_FlushDataCache(icons, icons_size);
GX_RequestDma((u32*) icons, tex->data, icons_size);
gspWaitForDMA();
Tex3DS_Texture t3x = Tex3DS_TextureImport(icons, icons_size, &guiFont->icons, NULL, true);
Tex3DS_TextureFree(t3x);
return guiFont;
}
@ -136,23 +133,28 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
y -= metric.height;
break;
}
s16 origin = font->icons.height - metric.y - metric.height;
switch (orient) {
case GUI_ORIENT_HMIRROR:
ctrAddRectEx(color, x + metric.width, y,
-metric.width, metric.height,
metric.x, metric.y,
ctrAddRectEx(color, x + metric.width, y + metric.height,
-metric.width, -metric.height,
metric.x, origin,
metric.width, metric.height, 0);
break;
case GUI_ORIENT_VMIRROR:
ctrAddRectEx(color, x, y + metric.height,
metric.width, -metric.height,
metric.x, metric.y,
ctrAddRectEx(color, x, y,
metric.width, metric.height,
metric.x, origin,
metric.width, metric.height, 0);
break;
case GUI_ORIENT_0:
default:
// TODO: Rotation
ctrAddRect(color, x, y, metric.x, metric.y, metric.width, metric.height);
ctrAddRectEx(color, x, y + metric.height,
metric.width, -metric.height,
metric.x, origin,
metric.width, metric.height, 0);
break;
}
}
@ -163,11 +165,10 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
if (icon >= GUI_ICON_MAX) {
return;
}
struct GUIIconMetric metric = defaultIconMetrics[icon];
ctrAddRectEx(color, x, y,
ctrAddRectEx(color, x, y + (h ? h : metric.height),
w ? w : metric.width,
h ? h : metric.height,
metric.x, metric.y,
h ? -h : -metric.height,
metric.x, font->icons.height - metric.y - metric.height,
metric.width, metric.height, 0);
}

Binary file not shown.

BIN
src/platform/3ds/icons.t3x Normal file

Binary file not shown.

View File

@ -28,6 +28,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
-lScePower_stub
-lSceSysmodule_stub
-lSceTouch_stub)
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
set(OBJCOPY_CMD ${OBJCOPY} -I binary -O elf32-littlearm -B arm)
list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c)
@ -61,5 +62,30 @@ vita_create_vpk(${BINARY_NAME}.vpk MGBA00001 ${BINARY_NAME}.self
FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png
FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml)
if(BUILD_PERF)
add_executable(perf-main.elf perf.c)
set_target_properties(perf-main.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
target_link_libraries(perf-main.elf ${OS_LIB})
vita_create_self(perf.bin ../${BINARY_NAME}-perf)
vita_create_self(perf-main.self perf-main.elf)
vita_create_vpk(${BINARY_NAME}-perf.vpk MGBA00002 perf-main.self
NAME "${PROJECT_NAME} Perf Tester"
FILE ${CMAKE_CURRENT_BINARY_DIR}/perf.bin perf.bin
FILE ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys/icon0.png
FILE ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys/pic0.png
FILE ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents/bg.png
FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png
FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml)
add_dependencies(${BINARY_NAME}-perf.vpk perf.bin)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/perf.bin
${CMAKE_CURRENT_SOURCE_DIR}/perf-start.sh
${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.vpk
DESTINATION . COMPONENT ${BINARY_NAME}-perf)
endif()
install(TARGETS ${BINARY_NAME}.elf DESTINATION . COMPONENT ${BINARY_NAME}-dbg)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.vpk DESTINATION . COMPONENT ${BINARY_NAME}-psp2)

17
src/platform/psp2/perf-start.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
IP=$1
shift
# Kill any previous session
echo | nc $IP 7215 > /dev/null
echo | nc $IP 7216 > /dev/null
curl -sT perf.bin ftp://$IP:1337/ux0:app/MGBA00002/
echo launch MGBA00002 | nc $IP 1338 > /dev/null
sleep 2
(for ARG in $@; do
echo $ARG
done; echo) | nc $IP 7215
exit 0

57
src/platform/psp2/perf.c Normal file
View File

@ -0,0 +1,57 @@
/* Copyright (c) 2013-2020 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-util/socket.h>
#include <psp2/appmgr.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#define MAX_ARGS 19
void connectServer(char* args[]) {
Socket server = SocketOpenTCP(7215, NULL);
if (SOCKET_FAILED(server)) {
return;
}
if (SOCKET_FAILED(SocketListen(server, 0))) {
SocketClose(server);
return;
}
Socket conn = SocketAccept(server, NULL);
if (SOCKET_FAILED(conn)) {
SocketClose(server);
return;
}
int i = 0;
ssize_t len;
char arg[1024];
while (i < MAX_ARGS && (len = SocketRecv(conn, arg, sizeof(arg) - 1)) > 0) {
arg[len] = '\0';
char* tok;
for (tok = strtok(arg, "\n"); tok && i < MAX_ARGS; ++i) {
args[i] = strdup(tok);
tok = strtok(NULL, "\n");
}
if (arg[len - 1] == '\n') {
return;
}
}
SocketClose(conn);
SocketClose(server);
}
int main() {
char* args[MAX_ARGS + 1] = { 0 };
SocketSubsystemInit();
connectServer(args);
SocketSubsystemDeinit();
sceAppMgrLoadExec("app0:/perf.bin", args, NULL);
sceKernelExitProcess(0);
return 0;
}

View File

@ -50,7 +50,6 @@ static enum ScreenMode {
SM_MAX
} screenMode;
static void* outputBuffer;
static int currentTex;
static vita2d_texture* tex[2];
static vita2d_texture* screenshot;
@ -493,7 +492,6 @@ void mPSP2Teardown(struct mGUIRunner* runner) {
vita2d_free_texture(tex[0]);
vita2d_free_texture(tex[1]);
vita2d_free_texture(screenshot);
mappedMemoryFree(outputBuffer, 256 * 256 * 4);
frameLimiter = true;
}

View File

@ -4,21 +4,11 @@
* 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-util/memory.h>
#include <mgba-util/vector.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/types.h>
DECLARE_VECTOR(SceUIDList, SceUID);
DEFINE_VECTOR(SceUIDList, SceUID);
static struct SceUIDList uids;
static bool listInit = false;
void* anonymousMemoryMap(size_t size) {
if (!listInit) {
SceUIDListInit(&uids, 8);
}
if (size & 0xFFF) {
// Align to 4kB pages
size += ((~size) & 0xFFF) + 1;
@ -27,7 +17,6 @@ void* anonymousMemoryMap(size_t size) {
if (memblock < 0) {
return 0;
}
*SceUIDListAppend(&uids) = memblock;
void* ptr;
if (sceKernelGetMemBlockBase(memblock, &ptr) < 0) {
return 0;
@ -36,18 +25,8 @@ void* anonymousMemoryMap(size_t size) {
}
void mappedMemoryFree(void* memory, size_t size) {
UNUSED(size);
size_t i;
for (i = 0; i < SceUIDListSize(&uids); ++i) {
SceUID uid = *SceUIDListGetPointer(&uids, i);
void* ptr;
if (sceKernelGetMemBlockBase(uid, &ptr) < 0) {
continue;
}
if (ptr == memory) {
sceKernelFreeMemBlock(uid);
SceUIDListShift(&uids, i, 1);
return;
}
SceUID uid = sceKernelFindMemBlockByAddr(memory, size);
if (uid >= 0) {
sceKernelFreeMemBlock(uid);
}
}

View File

@ -40,7 +40,7 @@ static void* _vfsceMap(struct VFile* vf, size_t size, int flags);
static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfsceTruncate(struct VFile* vf, size_t size);
static ssize_t _vfsceSize(struct VFile* vf);
static bool _vfsceSync(struct VFile* vf, const void* memory, size_t size);
static bool _vfsceSync(struct VFile* vf, void* memory, size_t size);
static bool _vdsceClose(struct VDir* vd);
static void _vdsceRewind(struct VDir* vd);
@ -146,13 +146,14 @@ ssize_t _vfsceSize(struct VFile* vf) {
return end;
}
bool _vfsceSync(struct VFile* vf, const void* buffer, size_t size) {
bool _vfsceSync(struct VFile* vf, void* buffer, size_t size) {
struct VFileSce* vfsce = (struct VFileSce*) vf;
if (buffer && size) {
SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR);
sceIoLseek(vfsce->fd, 0, SEEK_SET);
sceIoWrite(vfsce->fd, buffer, size);
int res = sceIoWrite(vfsce->fd, buffer, size);
sceIoLseek(vfsce->fd, cur, SEEK_SET);
return res == size;
}
return sceIoSyncByFd(vfsce->fd) >= 0;
}
@ -362,4 +363,4 @@ bool VDirCreate(const char* path) {
// TODO: Verify vitasdk explanation of return values
sceIoMkdir(path, 0777);
return true;
}
}

View File

@ -22,8 +22,36 @@ class _ARMRegisters:
class ARMCore:
SP = 13
LR = 14
PC = 15
def __init__(self, native):
self._native = ffi.cast("struct ARMCore*", native)
self.gprs = _ARMRegisters(self)
self.cpsr = self._native.cpsr
self.spsr = self._native.spsr
@property
def sp(self):
return self.gprs[self.SP]
@sp.setter
def sp(self, value):
self.gprs[self.SP] = value
@property
def lr(self):
return self.gprs[self.LR]
@lr.setter
def lr(self, value):
self.gprs[self.LR] = value
@property
def pc(self):
return self.gprs[self.PC]
@pc.setter
def pc(self, value):
self.gprs[self.PC] = value

View File

@ -73,8 +73,9 @@ def _vfpSync(vf, buffer, size):
if buffer and size:
pos = f.tell()
f.seek(0, os.SEEK_SET)
_vfpWrite(vf, buffer, size)
res = _vfpWrite(vf, buffer, size)
f.seek(pos, os.SEEK_SET)
return res == size
f.flush()
os.fsync()
return True
@ -114,6 +115,10 @@ class VFile:
self._no_gc = _no_gc
self._claimed = False
@staticmethod
def fromEmpty():
return VFile(lib.VFileMemChunk(ffi.NULL, 0))
def __del__(self):
if not self._claimed:
self.close()

View File

@ -22,4 +22,4 @@ PYEXPORT void* _vfpMap(struct VFile* vf, size_t size, int flags);
PYEXPORT void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
PYEXPORT void _vfpTruncate(struct VFile* vf, size_t size);
PYEXPORT ssize_t _vfpSize(struct VFile* vf);
PYEXPORT bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
PYEXPORT bool _vfpSync(struct VFile* vf, void* buffer, size_t size);

View File

@ -42,6 +42,9 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
#else
int size = QFontMetrics(QFont()).height() / (devicePixelRatio() * 12);
#endif
if (!size) {
size = 1;
}
m_ui.chipList->setIconSize(m_ui.chipList->iconSize() * size);
m_ui.chipList->setGridSize(m_ui.chipList->gridSize() * size);
m_model.setScale(size);
@ -259,4 +262,4 @@ void BattleChipView::updateData() {
m_updater = nullptr;
});
m_updater->downloadUpdate();
}
}

View File

@ -7,12 +7,16 @@
#include "CoreController.h"
#include "LogController.h"
#include "VFileDevice.h"
#include <QDir>
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
#endif
#ifdef M_CORE_GB
#include <mgba/gb/core.h>
#endif
#include <mgba/core/core.h>
#include <mgba-util/vfs.h>
@ -27,6 +31,57 @@ void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) {
m_multiplayer = multiplayer;
}
QByteArray CoreManager::getExtdata(const QString& filename, mStateExtdataTag extdataType) {
VFileDevice vf(filename, QIODevice::ReadOnly);
if (!vf.isOpen()) {
return {};
}
mStateExtdata extdata;
mStateExtdataInit(&extdata);
QByteArray bytes;
auto extract = [&bytes, &extdata, &vf, extdataType](mCore* core) -> bool {
if (mCoreExtractExtdata(core, vf, &extdata)) {
mStateExtdataItem extitem;
if (!mStateExtdataGet(&extdata, extdataType, &extitem)) {
return false;
}
if (extitem.size) {
bytes = QByteArray::fromRawData(static_cast<const char*>(extitem.data), extitem.size);
}
return true;
}
return false;
};
bool done = false;
struct mCore* core = nullptr;
#ifdef USE_PNG
done = extract(nullptr);
#endif
#ifdef M_CORE_GBA
if (!done) {
core = GBACoreCreate();
core->init(core);
done = extract(core);
core->deinit(core);
}
#endif
#ifdef M_CORE_GB
if (!done) {
core = GBCoreCreate();
core->init(core);
done = extract(core);
core->deinit(core);
}
#endif
mStateExtdataDeinit(&extdata);
return bytes;
}
CoreController* CoreManager::loadGame(const QString& path) {
QFileInfo info(path);
if (!info.isReadable()) {

View File

@ -9,6 +9,8 @@
#include <QObject>
#include <QString>
#include <mgba/core/serialize.h>
struct mCoreConfig;
struct VFile;
@ -25,6 +27,8 @@ public:
void setMultiplayerController(MultiplayerController*);
void setPreload(bool preload) { m_preload = preload; }
static QByteArray getExtdata(const QString& filename, mStateExtdataTag extdataType);
public slots:
CoreController* loadGame(const QString& path);
CoreController* loadGame(VFile* vf, const QString& path, const QString& base);

View File

@ -13,6 +13,97 @@
using namespace QGBA;
IntValidator::IntValidator(bool isSigned, QObject* parent)
: QValidator(parent)
, m_signed(isSigned)
{
}
QValidator::State IntValidator::validate(QString& input, int&) const {
if (input.isEmpty()) {
return QValidator::Intermediate;
}
if (input.size() == 1 && input[0] == '-') {
if (m_signed) {
return QValidator::Intermediate;
} else {
return QValidator::Invalid;
}
}
if (input[0].isSpace()) {
return QValidator::Invalid;
}
if (input[input.size() - 1].isSpace()) {
return QValidator::Invalid;
}
if (input.size() > 1 && input[0] == '0') {
return QValidator::Invalid;
}
bool ok = false;
qlonglong val = locale().toLongLong(input, &ok);
if (!ok) {
return QValidator::Invalid;
}
qlonglong hardmax;
qlonglong hardmin;
qlonglong max;
qlonglong min;
if (m_signed) {
switch (m_width) {
case 1:
hardmax = 999LL;
hardmin = -999LL;
max = 0x7FLL;
min = -0x80LL;
break;
case 2:
hardmax = 99999LL;
hardmin = -99999LL;
max = 0x7FFFLL;
min = -0x8000LL;
break;
case 4:
hardmax = 9999999999LL;
hardmin = -9999999999LL;
max = 0x7FFFFFFFLL;
min = -0x80000000LL;
break;
default:
return QValidator::Invalid;
}
} else {
hardmin = 0;
min = 0;
switch (m_width) {
case 1:
hardmax = 999LL;
max = 0xFFLL;
break;
case 2:
hardmax = 99999LL;
max = 0xFFFFLL;
break;
case 4:
hardmax = 9999999999LL;
max = 0xFFFFFFFFLL;
break;
default:
return QValidator::Invalid;
}
}
if (val < hardmin || val > hardmax) {
return QValidator::Invalid;
}
if (val < min || val > max) {
return QValidator::Intermediate;
}
return QValidator::Acceptable;
}
MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
@ -21,6 +112,9 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
m_ui.hexfield->setController(controller);
m_ui.sintVal->setValidator(&m_sintValidator);
m_ui.uintVal->setValidator(&m_uintValidator);
mCore* core = m_controller->thread()->core;
const mCoreMemoryBlock* info;
size_t nBlocks = core->listMemoryBlocks(core, &info);
@ -39,9 +133,21 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
}
}
connect(m_ui.width8, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(1); });
connect(m_ui.width16, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(2); });
connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); });
connect(m_ui.width8, &QAbstractButton::clicked, [this]() {
m_ui.hexfield->setAlignment(1);
m_sintValidator.setWidth(1);
m_uintValidator.setWidth(1);
});
connect(m_ui.width16, &QAbstractButton::clicked, [this]() {
m_ui.hexfield->setAlignment(2);
m_sintValidator.setWidth(2);
m_uintValidator.setWidth(2);
});
connect(m_ui.width32, &QAbstractButton::clicked, [this]() {
m_ui.hexfield->setAlignment(4);
m_sintValidator.setWidth(4);
m_uintValidator.setWidth(4);
});
connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress));
connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
@ -60,6 +166,41 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load);
connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL);
connect(m_ui.sintVal, &QLineEdit::returnPressed, this, [this]() {
int align = m_ui.hexfield->alignment();
mCore* core = m_controller->thread()->core;
int32_t value = m_ui.sintVal->text().toInt();
switch (align) {
case 1:
core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
break;
case 2:
core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
break;
case 4:
core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
break;
}
update();
});
connect(m_ui.uintVal, &QLineEdit::returnPressed, this, [this]() {
int align = m_ui.hexfield->alignment();
mCore* core = m_controller->thread()->core;
uint32_t value = m_ui.uintVal->text().toUInt();
switch (align) {
case 1:
core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
break;
case 2:
core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
break;
case 4:
core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
break;
}
update();
});
}
void MemoryView::setIndex(int index) {
@ -116,7 +257,9 @@ void MemoryView::updateStatus() {
if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
m_ui.sintVal->clear();
m_ui.sintVal->setReadOnly(true);
m_ui.uintVal->clear();
m_ui.uintVal->setReadOnly(true);
return;
}
union {
@ -144,6 +287,8 @@ void MemoryView::updateStatus() {
m_ui.uintVal->setText(QString::number(value.u32));
break;
}
m_ui.sintVal->setReadOnly(false);
m_ui.uintVal->setReadOnly(false);
}
void MemoryView::saveRange() {
@ -153,4 +298,4 @@ void MemoryView::saveRange() {
memdump->setSegment(m_ui.segments->value());
memdump->setByteCount(m_selection.second - m_selection.first);
memdump->show();
}
}

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include <QValidator>
#include "MemoryModel.h"
#include "ui_MemoryView.h"
@ -13,6 +15,20 @@ namespace QGBA {
class CoreController;
class IntValidator : public QValidator {
Q_OBJECT
public:
IntValidator(bool isSigned, QObject* parent = nullptr);
virtual QValidator::State validate(QString& input, int& pos) const override;
void setWidth(int bytes) { m_width = bytes; }
private:
int m_width = 1;
bool m_signed;
};
class MemoryView : public QWidget {
Q_OBJECT
@ -32,6 +48,8 @@ private slots:
private:
Ui::MemoryView m_ui;
IntValidator m_sintValidator{true};
IntValidator m_uintValidator{false};
std::shared_ptr<CoreController> m_controller;
QPair<uint32_t, uint32_t> m_region;

View File

@ -190,6 +190,9 @@
</item>
<item>
<widget class="QLineEdit" name="uintVal">
<property name="maxLength">
<number>10</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -208,6 +211,9 @@
</item>
<item>
<widget class="QLineEdit" name="sintVal">
<property name="maxLength">
<number>11</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@ -304,6 +310,23 @@
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>regions</tabstop>
<tabstop>segments</tabstop>
<tabstop>setAddress</tabstop>
<tabstop>width8</tabstop>
<tabstop>width16</tabstop>
<tabstop>width32</tabstop>
<tabstop>sintVal</tabstop>
<tabstop>uintVal</tabstop>
<tabstop>stringVal</tabstop>
<tabstop>loadTBL</tabstop>
<tabstop>copy</tabstop>
<tabstop>paste</tabstop>
<tabstop>save</tabstop>
<tabstop>saveRange</tabstop>
<tabstop>load</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -50,6 +50,8 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
s_mbcList.append(GB_HuC1);
s_mbcList.append(GB_HuC3);
s_mbcList.append(GB_UNL_WISDOM_TREE);
s_mbcList.append(GB_UNL_BBD);
s_mbcList.append(GB_UNL_HITEK);
s_mbcList.append(GB_UNL_PKJD);
}
if (s_gbModelList.isEmpty()) {

View File

@ -359,6 +359,16 @@
<string>Wisdom Tree (Unlicensed)</string>
</property>
</item>
<item>
<property name="text">
<string>BBD (Unlicensed)</string>
</property>
</item>
<item>
<property name="text">
<string>Hitek (Unlicensed)</string>
</property>
</item>
<item>
<property name="text">
<string>Pokémon Jade/Diamond (Unlicensed)</string>

View File

@ -19,6 +19,23 @@ VFileDevice::VFileDevice(VFile* vf, QObject* parent)
}
}
VFileDevice::VFileDevice(const QString& filename, QIODevice::OpenMode mode, QObject* parent)
: QIODevice(parent)
{
int posixMode = 0;
if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
posixMode = O_RDWR;
} else if (mode & QIODevice::ReadOnly) {
posixMode = O_RDONLY;
} else if (mode & QIODevice::WriteOnly) {
posixMode = O_WRONLY;
}
m_vf = open(filename, posixMode);
if (m_vf) {
setOpenMode(mode);
}
}
void VFileDevice::close() {
if (!m_vf) {
return;

View File

@ -17,6 +17,7 @@ Q_OBJECT
public:
VFileDevice(VFile* vf = nullptr, QObject* parent = nullptr);
VFileDevice(const QString&, QIODevice::OpenMode, QObject* parent = nullptr);
virtual void close() override;
virtual bool seek(qint64 pos) override;

View File

@ -1518,12 +1518,9 @@ void Window::setupMenu(QMenuBar* menubar) {
ConfigOption* mute = m_config->addOption("mute");
mute->addBoolean(tr("Mute"), &m_actions, "av");
mute->connect([this](const QVariant& value) {
if (value.toInt()) {
m_config->setOption("fastForwardMute", true);
}
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
reloadConfig();
}, this);
m_config->updateOption("mute");
m_actions.addMenu(tr("FPS target"),"target", "av");
ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1350,6 +1350,7 @@ void _log(struct mLogger* log, int category, enum mLogLevel level, const char* f
int main(int argc, char** argv) {
ThreadLocalInitKey(&logStream);
ThreadLocalSetKey(logStream, NULL);
putenv("TZ=UTC");
int status = 0;
if (!parseCInemaArgs(argc, argv)) {

View File

@ -31,6 +31,10 @@ uint32_t* romBuffer;
size_t romBufferSize;
#endif
#endif
#ifdef PSP2
#include <psp2/kernel/processmgr.h>
#include <psp2/power.h>
#endif
#include <errno.h>
#include <fcntl.h>
@ -108,6 +112,8 @@ int main(int argc, char** argv) {
romBuffer = SYS_GetArena2Lo();
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize));
#endif
#elif defined(PSP2)
scePowerSetArmClockFrequency(444);
#else
signal(SIGINT, _mPerfShutdown);
#endif
@ -177,6 +183,8 @@ int main(int argc, char** argv) {
VIDEO_Flush();
VIDEO_WaitVSync();
VIDEO_WaitVSync();
#elif defined(PSP2)
sceKernelExitProcess(0);
#endif
return didFail;

View File

@ -23,14 +23,24 @@ add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${OS_LIB})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
COMMAND ${GXTEXCONV} -i ${CMAKE_SOURCE_DIR}/res/font2x.png -o font.tpl colfmt=5 mipmap=no
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/font2x.png
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.c
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/wii/font.tpl
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/src/platform/wii/font.tpl
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
COMMAND ${GXTEXCONV} -i ${CMAKE_SOURCE_DIR}/res/icons2x.png -o icons.tpl colfmt=5 mipmap=no
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/icons2x.png
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/wii/icons.tpl
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/src/platform/wii/icons.tpl
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
add_custom_target(${BINARY_NAME}.dol ALL

Binary file not shown.

Binary file not shown.

View File

@ -152,4 +152,6 @@ const struct GUIIconMetric defaultIconMetrics[] = {
[GUI_ICON_BUTTON_TRIANGLE] = { 34, 34, 12, 11 },
[GUI_ICON_BUTTON_SQUARE] = { 50, 34, 12, 11 },
[GUI_ICON_BUTTON_HOME] = { 66, 34, 12, 11 },
[GUI_ICON_STATUS_FAST_FORWARD] = { 2, 50, 12, 12 },
[GUI_ICON_STATUS_MUTE] = { 17, 50, 14, 12 },
};

View File

@ -28,7 +28,9 @@ static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
// handle aligned reads, do the conversion here
static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) {
return p[i];
uint32_t ret;
LOAD_32LE(ret, i << 2, p);
return ret;
}
//-----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
/* Copyright (c) 2013-2020 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
@ -6,18 +6,18 @@
#include <mgba-util/table.h>
#include <mgba-util/hash.h>
#include <mgba-util/math.h>
#include <mgba-util/string.h>
#define LIST_INITIAL_SIZE 8
#define LIST_INITIAL_SIZE 4
#define TABLE_INITIAL_SIZE 8
#define REBALANCE_THRESHOLD 4
#define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key
#define HASH_TABLE_STRNCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && LIST->list[(INDEX)].keylen == keylen && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
#define TABLE_LOOKUP_START(COMPARATOR, LIST, KEY) \
uint32_t entry = (KEY) & (table->tableSize - 1); \
LIST = &table->table[entry]; \
#define TABLE_LOOKUP_START(COMPARATOR, LIST) \
size_t i; \
for (i = 0; i < LIST->nEntries; ++i) { \
if (COMPARATOR(LIST, i)) { \
@ -42,6 +42,18 @@ struct TableList {
size_t listSize;
};
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value);
static inline const struct TableList* _getConstList(const struct Table* table, uint32_t key) {
uint32_t entry = key & (table->tableSize - 1);
return &table->table[entry];
}
static inline struct TableList* _getList(struct Table* table, uint32_t key) {
uint32_t entry = key & (table->tableSize - 1);
return &table->table[entry];
}
static struct TableList* _resizeAsNeeded(struct Table* table, struct TableList* list, uint32_t key) {
UNUSED(table);
UNUSED(key);
@ -65,14 +77,36 @@ static void _removeItemFromList(struct Table* table, struct TableList* list, siz
}
}
void TableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
if (initialSize < 2 || (initialSize & (initialSize - 1))) {
static void _rebalance(struct Table* table) {
struct Table newTable;
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
newTable.seed = table->seed * 134775813 + 1;
size_t i;
for (i = 0; i < table->tableSize; ++i) {
const struct TableList* list = &table->table[i];
size_t j;
for (j = 0; j < list->nEntries; ++j) {
HashTableInsertBinaryMoveKey(&newTable, list->list[j].stringKey, list->list[j].keylen, list->list[j].value);
}
free(list->list);
}
free(table->table);
table->tableSize = newTable.tableSize;
table->table = newTable.table;
table->seed = newTable.seed;
}
void TableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
if (initialSize < 2) {
initialSize = TABLE_INITIAL_SIZE;
} else if (initialSize & (initialSize - 1)) {
initialSize = toPow2(initialSize);
}
table->tableSize = initialSize;
table->table = calloc(table->tableSize, sizeof(struct TableList));
table->size = 0;
table->deinitializer = deinitializer;
table->seed = 0;
size_t i;
for (i = 0; i < table->tableSize; ++i) {
@ -101,16 +135,16 @@ void TableDeinit(struct Table* table) {
}
void* TableLookup(const struct Table* table, uint32_t key) {
const struct TableList* list;
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
const struct TableList* list = _getConstList(table, key);
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
return lookupResult->value;
} TABLE_LOOKUP_END;
return 0;
}
void TableInsert(struct Table* table, uint32_t key, void* value) {
struct TableList* list;
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
struct TableList* list = _getList(table, key);
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
@ -128,8 +162,8 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
}
void TableRemove(struct Table* table, uint32_t key) {
struct TableList* list;
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
struct TableList* list = _getList(table, key);
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
} TABLE_LOOKUP_END;
}
@ -151,7 +185,7 @@ void TableClear(struct Table* table) {
}
}
void TableEnumerate(const struct Table* table, void (handler(uint32_t key, void* value, void* user)), void* user) {
void TableEnumerate(const struct Table* table, void (*handler)(uint32_t key, void* value, void* user), void* user) {
size_t i;
for (i = 0; i < table->tableSize; ++i) {
const struct TableList* list = &table->table[i];
@ -166,8 +200,9 @@ size_t TableSize(const struct Table* table) {
return table->size;
}
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
TableInit(table, initialSize, deinitializer);
table->seed = 1;
}
void HashTableDeinit(struct Table* table) {
@ -175,27 +210,32 @@ void HashTableDeinit(struct Table* table) {
}
void* HashTableLookup(const struct Table* table, const char* key) {
uint32_t hash = hash32(key, strlen(key), 0);
const struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, strlen(key), table->seed);
const struct TableList* list = _getConstList(table, hash);
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
return lookupResult->value;
} TABLE_LOOKUP_END;
return 0;
}
void* HashTableLookupBinary(const struct Table* table, const void* key, size_t keylen) {
uint32_t hash = hash32(key, keylen, 0);
const struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, keylen, table->seed);
const struct TableList* list = _getConstList(table, hash);
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
return lookupResult->value;
} TABLE_LOOKUP_END;
return 0;
}
void HashTableInsert(struct Table* table, const char* key, void* value) {
uint32_t hash = hash32(key, strlen(key), 0);
struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, strlen(key), table->seed);
struct TableList* list = _getList(table, hash);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_rebalance(table);
hash = hash32(key, strlen(key), table->seed);
list = _getList(table, hash);
}
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
@ -214,9 +254,14 @@ void HashTableInsert(struct Table* table, const char* key, void* value) {
}
void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen, void* value) {
uint32_t hash = hash32(key, keylen, 0);
struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, keylen, table->seed);
struct TableList* list = _getList(table, hash);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_rebalance(table);
hash = hash32(key, keylen, table->seed);
list = _getList(table, hash);
}
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
@ -235,18 +280,44 @@ void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen,
++table->size;
}
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value) {
uint32_t hash = hash32(key, keylen, table->seed);
struct TableList* list = _getList(table, hash);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_rebalance(table);
hash = hash32(key, keylen, table->seed);
list = _getList(table, hash);
}
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
}
lookupResult->value = value;
}
return;
} TABLE_LOOKUP_END;
list = _resizeAsNeeded(table, list, hash);
list->list[list->nEntries].key = hash;
list->list[list->nEntries].stringKey = key;
list->list[list->nEntries].keylen = keylen;
list->list[list->nEntries].value = value;
++list->nEntries;
++table->size;
}
void HashTableRemove(struct Table* table, const char* key) {
uint32_t hash = hash32(key, strlen(key), 0);
struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, strlen(key), table->seed);
struct TableList* list = _getList(table, hash);
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
} TABLE_LOOKUP_END;
}
void HashTableRemoveBinary(struct Table* table, const void* key, size_t keylen) {
uint32_t hash = hash32(key, keylen, 0);
struct TableList* list;
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
uint32_t hash = hash32(key, keylen, table->seed);
struct TableList* list = _getList(table, hash);
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
} TABLE_LOOKUP_END;
}
@ -269,7 +340,7 @@ void HashTableClear(struct Table* table) {
}
}
void HashTableEnumerate(const struct Table* table, void (handler(const char* key, void* value, void* user)), void* user) {
void HashTableEnumerate(const struct Table* table, void (*handler)(const char* key, void* value, void* user), void* user) {
size_t i;
for (i = 0; i < table->tableSize; ++i) {
const struct TableList* list = &table->table[i];
@ -280,7 +351,18 @@ void HashTableEnumerate(const struct Table* table, void (handler(const char* key
}
}
const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user) {
void HashTableEnumerateBinary(const struct Table* table, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user) {
size_t i;
for (i = 0; i < table->tableSize; ++i) {
const struct TableList* list = &table->table[i];
size_t j;
for (j = 0; j < list->nEntries; ++j) {
handler(list->list[j].stringKey, list->list[j].keylen, list->list[j].value, user);
}
}
}
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user) {
size_t i;
const char* result = NULL;
for (i = 0; i < table->tableSize; ++i) {

View File

@ -42,7 +42,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags);
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfdTruncate(struct VFile* vf, size_t size);
static ssize_t _vfdSize(struct VFile* vf);
static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vfdSync(struct VFile* vf, void* buffer, size_t size);
struct VFile* VFileOpenFD(const char* path, int flags) {
if (!path) {
@ -195,7 +195,7 @@ static ssize_t _vfdSize(struct VFile* vf) {
return stat.st_size;
}
static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
static bool _vfdSync(struct VFile* vf, void* buffer, size_t size) {
UNUSED(buffer);
UNUSED(size);
struct VFileFD* vfd = (struct VFileFD*) vf;
@ -206,7 +206,7 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
futimes(vfd->fd, NULL);
#endif
if (buffer && size) {
return msync(buffer, size, MS_SYNC) == 0;
return msync(buffer, size, MS_ASYNC) == 0;
}
return fsync(vfd->fd) == 0;
#else
@ -217,7 +217,7 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
SystemTimeToFileTime(&st, &ft);
SetFileTime(h, NULL, &ft, &ft);
if (buffer && size) {
FlushViewOfFile(buffer, size);
return FlushViewOfFile(buffer, size);
}
return FlushFileBuffers(h);
#endif

View File

@ -19,7 +19,7 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags);
static void _vffUnmap(struct VFile* vf, void* memory, size_t size);
static void _vffTruncate(struct VFile* vf, size_t size);
static ssize_t _vffSize(struct VFile* vf);
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vffSync(struct VFile* vf, void* buffer, size_t size);
struct VFile* VFileFIFO(struct CircleBuffer* backing) {
if (!backing) {
@ -94,7 +94,7 @@ static ssize_t _vffSize(struct VFile* vf) {
return CircleBufferSize(vff->backing);
}
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) {
static bool _vffSync(struct VFile* vf, void* buffer, size_t size) {
UNUSED(vf);
UNUSED(buffer);
UNUSED(size);

View File

@ -24,7 +24,7 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags);
static void _vffUnmap(struct VFile* vf, void* memory, size_t size);
static void _vffTruncate(struct VFile* vf, size_t size);
static ssize_t _vffSize(struct VFile* vf);
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vffSync(struct VFile* vf, void* buffer, size_t size);
struct VFile* VFileFOpen(const char* path, const char* mode) {
if (!path && !mode) {
@ -144,13 +144,14 @@ static ssize_t _vffSize(struct VFile* vf) {
return size;
}
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) {
static bool _vffSync(struct VFile* vf, void* buffer, size_t size) {
struct VFileFILE* vff = (struct VFileFILE*) vf;
if (buffer && size) {
long pos = ftell(vff->file);
fseek(vff->file, 0, SEEK_SET);
fwrite(buffer, size, 1, vff->file);
size_t res = fwrite(buffer, size, 1, vff->file);
fseek(vff->file, pos, SEEK_SET);
return res == 1;
}
return fflush(vff->file) == 0;
}

View File

@ -58,7 +58,7 @@ static void* _vf7zMap(struct VFile* vf, size_t size, int flags);
static void _vf7zUnmap(struct VFile* vf, void* memory, size_t size);
static void _vf7zTruncate(struct VFile* vf, size_t size);
static ssize_t _vf7zSize(struct VFile* vf);
static bool _vf7zSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vf7zSync(struct VFile* vf, void* buffer, size_t size);
static bool _vd7zClose(struct VDir* vd);
static void _vd7zRewind(struct VDir* vd);
@ -327,7 +327,7 @@ bool _vd7zDeleteFile(struct VDir* vd, const char* path) {
return false;
}
bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) {
bool _vf7zSync(struct VFile* vf, void* memory, size_t size) {
UNUSED(vf);
UNUSED(memory);
UNUSED(size);

View File

@ -28,7 +28,7 @@ static void _vfmUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfmTruncate(struct VFile* vf, size_t size);
static void _vfmTruncateNoop(struct VFile* vf, size_t size);
static ssize_t _vfmSize(struct VFile* vf);
static bool _vfmSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vfmSync(struct VFile* vf, void* buffer, size_t size);
struct VFile* VFileFromMemory(void* mem, size_t size) {
if (!mem || !size) {
@ -297,7 +297,7 @@ ssize_t _vfmSize(struct VFile* vf) {
return vfm->size;
}
bool _vfmSync(struct VFile* vf, const void* buffer, size_t size) {
bool _vfmSync(struct VFile* vf, void* buffer, size_t size) {
UNUSED(vf);
UNUSED(buffer);
UNUSED(size);

View File

@ -74,7 +74,7 @@ static void* _vfzMap(struct VFile* vf, size_t size, int flags);
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
static void _vfzTruncate(struct VFile* vf, size_t size);
static ssize_t _vfzSize(struct VFile* vf);
static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
static bool _vfzSync(struct VFile* vf, void* buffer, size_t size);
static bool _vdzClose(struct VDir* vd);
static void _vdzRewind(struct VDir* vd);
@ -426,7 +426,7 @@ bool _vdzDeleteFile(struct VDir* vd, const char* path) {
return false;
}
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
bool _vfzSync(struct VFile* vf, void* memory, size_t size) {
UNUSED(vf);
UNUSED(memory);
UNUSED(size);
@ -654,7 +654,7 @@ bool _vdzDeleteFile(struct VDir* vd, const char* path) {
return false;
}
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
bool _vfzSync(struct VFile* vf, void* memory, size_t size) {
UNUSED(vf);
UNUSED(memory);
UNUSED(size);

View File

@ -72,18 +72,20 @@ class GameClockTest(PerfTest):
class PerfServer(object):
ITERATIONS_PER_INSTANCE = 50
def __init__(self, address, command=None):
def __init__(self, address, root='/', command=None):
s = address.rsplit(':', 1)
if len(s) == 1:
self.address = (s[0], 7216)
else:
self.address = (s[0], s[1])
self.command = None
if command:
self.command = shlex.split(command)
self.iterations = self.ITERATIONS_PER_INSTANCE
self.socket = None
self.results = []
self.reader = None
self.root = root
def _start(self, test):
if self.command:
@ -108,7 +110,7 @@ class PerfServer(object):
def run(self, test):
if not self.socket:
self._start(test)
self.socket.send(os.path.join("/perfroms", test.rom).encode("utf-8"))
self.socket.send(os.path.join(self.root, test.rom).encode("utf-8"))
self.results.append(next(self.reader))
self.iterations -= 1
if self.iterations == 0:
@ -178,6 +180,7 @@ if __name__ == '__main__':
parser.add_argument('-s', '--server', metavar='ADDRESS', help='run on server')
parser.add_argument('-S', '--server-command', metavar='COMMAND', help='command to launch server')
parser.add_argument('-o', '--out', metavar='FILE', help='output file path')
parser.add_argument('-r', '--root', metavar='PATH', type=str, default='/perfroms', help='root path for server mode')
parser.add_argument('directory', help='directory containing ROM files')
args = parser.parse_args()
@ -189,9 +192,9 @@ if __name__ == '__main__':
s = Suite(args.directory, wall=args.wall_time, game=args.game_frames, renderer=renderer)
if args.server:
if args.server_command:
server = PerfServer(args.server, args.server_command)
server = PerfServer(args.server, args.root, args.server_command)
else:
server = PerfServer(args.server)
server = PerfServer(args.server, args.root)
s.set_server(server)
s.collect_tests()
results = s.run()