Merge branch 'master' (early part) into medusa
5
CHANGES
|
@ -42,6 +42,7 @@ Emulation fixes:
|
||||||
- GB Timer: Fix timing adjustments when writing to TAC (fixes mgba.io/i/1340)
|
- GB Timer: Fix timing adjustments when writing to TAC (fixes mgba.io/i/1340)
|
||||||
- GBA Memory: Fix writing to OBJ memory in modes 3 and 5
|
- GBA Memory: Fix writing to OBJ memory in modes 3 and 5
|
||||||
- GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400)
|
- GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400)
|
||||||
|
- GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743)
|
||||||
Other fixes:
|
Other fixes:
|
||||||
- Qt: More app metadata fixes
|
- Qt: More app metadata fixes
|
||||||
- Qt: Fix load recent from archive (fixes mgba.io/i/1325)
|
- Qt: Fix load recent from archive (fixes mgba.io/i/1325)
|
||||||
|
@ -57,6 +58,8 @@ Other fixes:
|
||||||
- Qt: Fix adjusting magnification in tile viewer when not fitting to window
|
- Qt: Fix adjusting magnification in tile viewer when not fitting to window
|
||||||
- FFmpeg: Improve initialization reliability and cleanup
|
- FFmpeg: Improve initialization reliability and cleanup
|
||||||
- Wii: Fix aspect ratio (fixes mgba.io/i/500)
|
- Wii: Fix aspect ratio (fixes mgba.io/i/500)
|
||||||
|
- Qt: Fix some Qt display driver race conditions
|
||||||
|
- FFmpeg: Fix audio conversion producing gaps
|
||||||
Misc:
|
Misc:
|
||||||
- GBA Savedata: EEPROM performance fixes
|
- GBA Savedata: EEPROM performance fixes
|
||||||
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||||
|
@ -74,6 +77,8 @@ Misc:
|
||||||
- Debugger: Make tracing compatible with breakpoints/watchpoints
|
- Debugger: Make tracing compatible with breakpoints/watchpoints
|
||||||
- Debugger: Print breakpoint/watchpoint number when inserting
|
- Debugger: Print breakpoint/watchpoint number when inserting
|
||||||
- Qt: Open a message box for Qt frontend errors
|
- Qt: Open a message box for Qt frontend errors
|
||||||
|
- GBA Video: Clean up dead code in sprite rendering loop
|
||||||
|
- FFmpeg: Support audio-only recording
|
||||||
|
|
||||||
0.7.1: (2019-02-24)
|
0.7.1: (2019-02-24)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
|
@ -81,17 +81,33 @@ typedef intptr_t ssize_t;
|
||||||
#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
|
#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
|
||||||
#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
||||||
#define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE)
|
#define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
|
#define ATOMIC_SUB(DST, OP) __atomic_sub_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
#define ATOMIC_OR(DST, OP) __atomic_or_fetch(&DST, OP, __ATOMIC_RELEASE)
|
#define ATOMIC_OR(DST, OP) __atomic_or_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
#define ATOMIC_AND(DST, OP) __atomic_and_fetch(&DST, OP, __ATOMIC_RELEASE)
|
#define ATOMIC_AND(DST, OP) __atomic_and_fetch(&DST, OP, __ATOMIC_RELEASE)
|
||||||
#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) __atomic_compare_exchange_n(&DST, &EXPECTED, SRC, true,__ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) __atomic_compare_exchange_n(&DST, &EXPECTED, SRC, true,__ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||||
|
#elif defined _MSC_VER
|
||||||
|
#define ATOMIC_STORE(DST, SRC) InterlockedExchange(&DST, SRC)
|
||||||
|
#define ATOMIC_LOAD(DST, SRC) DST = InterlockedOrAcquire(&SRC, 0)
|
||||||
|
#define ATOMIC_ADD(DST, OP) InterlockedAddRelease(&DST, OP)
|
||||||
|
#define ATOMIC_SUB(DST, OP) InterlockedAddRelease(&DST, -OP)
|
||||||
|
#define ATOMIC_OR(DST, OP) InterlockedOrRelease(&DST, OP)
|
||||||
|
#define ATOMIC_AND(DST, OP) InterlockedAndRelease(&DST, OP)
|
||||||
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) (InterlockedCompareExchange(&DST, SRC, EXPECTED) == EXPECTED)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) InterlockedExchangePointer(DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(SRC, 0, 0)
|
||||||
#else
|
#else
|
||||||
// TODO
|
// TODO
|
||||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||||
#define ATOMIC_ADD(DST, OP) DST += OP
|
#define ATOMIC_ADD(DST, OP) DST += OP
|
||||||
|
#define ATOMIC_SUB(DST, OP) DST -= OP
|
||||||
#define ATOMIC_OR(DST, OP) DST |= OP
|
#define ATOMIC_OR(DST, OP) DST |= OP
|
||||||
#define ATOMIC_AND(DST, OP) DST &= OP
|
#define ATOMIC_AND(DST, OP) DST &= OP
|
||||||
#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false)
|
#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false)
|
||||||
|
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||||
|
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
|
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
|
||||||
|
|
|
@ -23,10 +23,14 @@ struct mLockstep {
|
||||||
enum mLockstepPhase transferActive;
|
enum mLockstepPhase transferActive;
|
||||||
int32_t transferCycles;
|
int32_t transferCycles;
|
||||||
|
|
||||||
|
void (*lock)(struct mLockstep*);
|
||||||
|
void (*unlock)(struct mLockstep*);
|
||||||
|
|
||||||
bool (*signal)(struct mLockstep*, unsigned mask);
|
bool (*signal)(struct mLockstep*, unsigned mask);
|
||||||
bool (*wait)(struct mLockstep*, unsigned mask);
|
bool (*wait)(struct mLockstep*, unsigned mask);
|
||||||
void (*addCycles)(struct mLockstep*, int id, int32_t cycles);
|
void (*addCycles)(struct mLockstep*, int id, int32_t cycles);
|
||||||
int32_t (*useCycles)(struct mLockstep*, int id, int32_t cycles);
|
int32_t (*useCycles)(struct mLockstep*, int id, int32_t cycles);
|
||||||
|
int32_t (*unusedCycles)(struct mLockstep*, int id);
|
||||||
void (*unload)(struct mLockstep*, int id);
|
void (*unload)(struct mLockstep*, int id);
|
||||||
void* context;
|
void* context;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -35,6 +39,19 @@ struct mLockstep {
|
||||||
};
|
};
|
||||||
|
|
||||||
void mLockstepInit(struct mLockstep*);
|
void mLockstepInit(struct mLockstep*);
|
||||||
|
void mLockstepDeinit(struct mLockstep*);
|
||||||
|
|
||||||
|
static inline void mLockstepLock(struct mLockstep* lockstep) {
|
||||||
|
if (lockstep->lock) {
|
||||||
|
lockstep->lock(lockstep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mLockstepUnlock(struct mLockstep* lockstep) {
|
||||||
|
if (lockstep->unlock) {
|
||||||
|
lockstep->unlock(lockstep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ struct ARMMemory {
|
||||||
uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
||||||
int* cycleCounter);
|
int* cycleCounter);
|
||||||
|
|
||||||
uint32_t* activeRegion;
|
const uint32_t* activeRegion;
|
||||||
uint32_t activeMask;
|
uint32_t activeMask;
|
||||||
uint32_t activeSeqCycles32;
|
uint32_t activeSeqCycles32;
|
||||||
uint32_t activeSeqCycles16;
|
uint32_t activeSeqCycles16;
|
||||||
|
|
|
@ -56,7 +56,7 @@ struct LR35902Memory {
|
||||||
|
|
||||||
int (*currentSegment)(struct LR35902Core*, uint16_t address);
|
int (*currentSegment)(struct LR35902Core*, uint16_t address);
|
||||||
|
|
||||||
uint8_t* activeRegion;
|
const uint8_t* activeRegion;
|
||||||
uint16_t activeMask;
|
uint16_t activeMask;
|
||||||
uint16_t activeRegionEnd;
|
uint16_t activeRegionEnd;
|
||||||
void (*setActiveRegion)(struct LR35902Core*, uint16_t address);
|
void (*setActiveRegion)(struct LR35902Core*, uint16_t address);
|
||||||
|
|
|
@ -230,7 +230,7 @@ bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
|
||||||
char directive[64];
|
char directive[64];
|
||||||
ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
|
ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
|
||||||
if (len > 1) {
|
if (len > 1) {
|
||||||
vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : len);
|
vf->write(vf, directive, (size_t) len > sizeof(directive) ? sizeof(directive) : (size_t) len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,13 +230,13 @@ bool mCoreLoadState(struct mCore* core, int slot, int flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) {
|
struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) {
|
||||||
char name[PATH_MAX];
|
char name[PATH_MAX + 14]; // Quash warning
|
||||||
snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
|
snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
|
||||||
return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreDeleteState(struct mCore* core, int slot) {
|
void mCoreDeleteState(struct mCore* core, int slot) {
|
||||||
char name[PATH_MAX];
|
char name[PATH_MAX + 14]; // Quash warning
|
||||||
snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
|
snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
|
||||||
core->dirs.state->deleteFile(core->dirs.state, name);
|
core->dirs.state->deleteFile(core->dirs.state, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,7 +341,7 @@ static void _mLibraryDeleteEntry(struct mLibrary* library, struct mLibraryEntry*
|
||||||
}
|
}
|
||||||
|
|
||||||
void mLibraryClear(struct mLibrary* library) {
|
void mLibraryClear(struct mLibrary* library) {
|
||||||
int result = sqlite3_exec(library->db,
|
sqlite3_exec(library->db,
|
||||||
" BEGIN TRANSACTION;"
|
" BEGIN TRANSACTION;"
|
||||||
"\n DELETE FROM roots;"
|
"\n DELETE FROM roots;"
|
||||||
"\n DELETE FROM roms;"
|
"\n DELETE FROM roms;"
|
||||||
|
|
|
@ -11,6 +11,12 @@ void mLockstepInit(struct mLockstep* lockstep) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
lockstep->transferId = 0;
|
lockstep->transferId = 0;
|
||||||
#endif
|
#endif
|
||||||
|
lockstep->lock = NULL;
|
||||||
|
lockstep->unlock = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mLockstepDeinit(struct mLockstep* lockstep) {
|
||||||
|
UNUSED(lockstep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Migrate nodes
|
// TODO: Migrate nodes
|
||||||
|
|
|
@ -14,6 +14,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextE
|
||||||
}
|
}
|
||||||
|
|
||||||
void mTimingDeinit(struct mTiming* timing) {
|
void mTimingDeinit(struct mTiming* timing) {
|
||||||
|
UNUSED(timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mTimingClear(struct mTiming* timing) {
|
void mTimingClear(struct mTiming* timing) {
|
||||||
|
|
|
@ -263,6 +263,8 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
|
||||||
free((void*) dbType);
|
free((void*) dbType);
|
||||||
free((void*) dbVersion);
|
free((void*) dbVersion);
|
||||||
|
|
||||||
|
sqlite3_finalize(gamedbTable);
|
||||||
|
sqlite3_finalize(gamedbDrop);
|
||||||
sqlite3_finalize(gameTable);
|
sqlite3_finalize(gameTable);
|
||||||
sqlite3_finalize(romTable);
|
sqlite3_finalize(romTable);
|
||||||
|
|
||||||
|
@ -275,6 +277,7 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoIntroDBDestroy(struct NoIntroDB* db) {
|
void NoIntroDBDestroy(struct NoIntroDB* db) {
|
||||||
|
sqlite3_finalize(db->crc32);
|
||||||
sqlite3_close(db->db);
|
sqlite3_close(db->db);
|
||||||
free(db);
|
free(db);
|
||||||
}
|
}
|
||||||
|
|
|
@ -508,7 +508,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
|
||||||
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy == y && wx <= endX) {
|
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy == y && wx <= endX) {
|
||||||
softwareRenderer->hasWindow = true;
|
softwareRenderer->hasWindow = true;
|
||||||
}
|
}
|
||||||
if (softwareRenderer->hasWindow && wx <= endX) {
|
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->hasWindow && wx <= endX) {
|
||||||
if (wx > 0 && !softwareRenderer->d.disableBG) {
|
if (wx > 0 && !softwareRenderer->d.disableBG) {
|
||||||
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy);
|
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu
|
||||||
static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
|
static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
|
||||||
|
|
||||||
void GBSIOLockstepInit(struct GBSIOLockstep* lockstep) {
|
void GBSIOLockstepInit(struct GBSIOLockstep* lockstep) {
|
||||||
mLockstepInit(&lockstep->d);
|
|
||||||
lockstep->players[0] = NULL;
|
lockstep->players[0] = NULL;
|
||||||
lockstep->players[1] = NULL;
|
lockstep->players[1] = NULL;
|
||||||
lockstep->pendingSB[0] = 0xFF;
|
lockstep->pendingSB[0] = 0xFF;
|
||||||
|
@ -236,6 +235,8 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu
|
||||||
mTimingDeschedule(&driver->p->p->timing, &driver->p->event);
|
mTimingDeschedule(&driver->p->p->timing, &driver->p->event);
|
||||||
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
||||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||||
|
} else {
|
||||||
|
mLOG(GB_SIO, FATAL, "GBSIOLockstepNodeWriteSC() failed to write to masterClaimed\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -94,6 +94,7 @@ void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
|
UNUSED(timing);
|
||||||
struct GBASIOBattlechipGate* gate = user;
|
struct GBASIOBattlechipGate* gate = user;
|
||||||
|
|
||||||
if (gate->d.p->mode == SIO_NORMAL_32) {
|
if (gate->d.p->mode == SIO_NORMAL_32) {
|
||||||
|
|
|
@ -390,11 +390,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
||||||
wait += waitstatesRegion[REGION_PALETTE_RAM];
|
wait += waitstatesRegion[REGION_PALETTE_RAM];
|
||||||
|
|
||||||
#define LOAD_VRAM \
|
#define LOAD_VRAM \
|
||||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
if ((address & 0x0001FFFF) >= SIZE_VRAM) { \
|
||||||
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \
|
||||||
} else { \
|
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load32: 0x%08X", address); \
|
||||||
LOAD_32(value, address & 0x00017FFC, gba->video.vram); \
|
value = 0; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
address &= 0x00017FFC; \
|
||||||
} \
|
} \
|
||||||
|
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||||
wait += waitstatesRegion[REGION_VRAM];
|
wait += waitstatesRegion[REGION_VRAM];
|
||||||
|
|
||||||
#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw);
|
#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw);
|
||||||
|
@ -520,11 +524,15 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
|
LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);
|
||||||
break;
|
break;
|
||||||
case REGION_VRAM:
|
case REGION_VRAM:
|
||||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||||
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||||
} else {
|
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load16: 0x%08X", address);
|
||||||
LOAD_16(value, address & 0x00017FFE, gba->video.vram);
|
value = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
address &= 0x00017FFE;
|
||||||
}
|
}
|
||||||
|
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||||
break;
|
break;
|
||||||
case REGION_OAM:
|
case REGION_OAM:
|
||||||
LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||||
|
@ -631,11 +639,15 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)];
|
value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)];
|
||||||
break;
|
break;
|
||||||
case REGION_VRAM:
|
case REGION_VRAM:
|
||||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||||
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||||
} else {
|
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load8: 0x%08X", address);
|
||||||
value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF];
|
value = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
address &= 0x00017FFF;
|
||||||
}
|
}
|
||||||
|
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
||||||
break;
|
break;
|
||||||
case REGION_OAM:
|
case REGION_OAM:
|
||||||
value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)];
|
value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)];
|
||||||
|
@ -717,20 +729,18 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
wait += waitstatesRegion[REGION_PALETTE_RAM];
|
wait += waitstatesRegion[REGION_PALETTE_RAM];
|
||||||
|
|
||||||
#define STORE_VRAM \
|
#define STORE_VRAM \
|
||||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
if ((address & 0x0001FFFF) >= SIZE_VRAM) { \
|
||||||
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \
|
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \
|
||||||
if (oldValue != value) { \
|
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store32: 0x%08X", address); \
|
||||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
break; \
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
|
||||||
} \
|
|
||||||
} else { \
|
|
||||||
LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \
|
|
||||||
if (oldValue != value) { \
|
|
||||||
STORE_32(value, address & 0x00017FFC, gba->video.vram); \
|
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \
|
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \
|
|
||||||
} \
|
} \
|
||||||
|
address &= 0x00017FFC; \
|
||||||
|
} \
|
||||||
|
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \
|
||||||
|
if (oldValue != value) { \
|
||||||
|
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||||
|
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
||||||
|
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||||
} \
|
} \
|
||||||
wait += waitstatesRegion[REGION_VRAM];
|
wait += waitstatesRegion[REGION_VRAM];
|
||||||
|
|
||||||
|
@ -840,18 +850,17 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REGION_VRAM:
|
case REGION_VRAM:
|
||||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||||
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
|
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||||
if (value != oldValue) {
|
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store16: 0x%08X", address);
|
||||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
break;
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram);
|
|
||||||
if (value != oldValue) {
|
|
||||||
STORE_16(value, address & 0x00017FFE, gba->video.vram);
|
|
||||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
|
|
||||||
}
|
}
|
||||||
|
address &= 0x00017FFE;
|
||||||
|
}
|
||||||
|
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
|
||||||
|
if (value != oldValue) {
|
||||||
|
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||||
|
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REGION_OAM:
|
case REGION_OAM:
|
||||||
|
@ -939,7 +948,6 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
||||||
break;
|
break;
|
||||||
case REGION_VRAM:
|
case REGION_VRAM:
|
||||||
if ((address & 0x0001FFFF) >= ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) {
|
if ((address & 0x0001FFFF) >= ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) {
|
||||||
// TODO: check BG mode
|
|
||||||
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);
|
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,12 +67,8 @@
|
||||||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||||
current = renderer->spriteLayer[outX]; \
|
current = renderer->spriteLayer[outX]; \
|
||||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||||
if (tileData) { \
|
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
|
||||||
} else if (current != FLAG_UNWRITTEN) { \
|
|
||||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \
|
#define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \
|
||||||
|
@ -84,13 +80,9 @@
|
||||||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||||
current = renderer->spriteLayer[outX]; \
|
current = renderer->spriteLayer[outX]; \
|
||||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||||
if (tileData) { \
|
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
renderer->spriteLayer[outX] = color | flags; \
|
||||||
renderer->spriteLayer[outX] = color | flags; \
|
|
||||||
} else if (current != FLAG_UNWRITTEN) { \
|
|
||||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
|
#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
|
||||||
|
@ -117,12 +109,8 @@
|
||||||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||||
current = renderer->spriteLayer[outX]; \
|
current = renderer->spriteLayer[outX]; \
|
||||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||||
if (tileData) { \
|
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
|
||||||
} else if (current != FLAG_UNWRITTEN) { \
|
|
||||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \
|
#define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \
|
||||||
|
@ -134,13 +122,9 @@
|
||||||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||||
current = renderer->spriteLayer[outX]; \
|
current = renderer->spriteLayer[outX]; \
|
||||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||||
if (tileData) { \
|
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
renderer->spriteLayer[outX] = color | flags; \
|
||||||
renderer->spriteLayer[outX] = color | flags; \
|
|
||||||
} else if (current != FLAG_UNWRITTEN) { \
|
|
||||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
|
#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
#include <mgba/internal/gba/gba.h>
|
#include <mgba/internal/gba/gba.h>
|
||||||
#include <mgba/internal/gba/io.h>
|
#include <mgba/internal/gba/io.h>
|
||||||
|
|
||||||
#define LOCKSTEP_INCREMENT 3000
|
#define LOCKSTEP_INCREMENT 2000
|
||||||
|
#define LOCKSTEP_TRANSFER 512
|
||||||
|
|
||||||
static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver);
|
static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver);
|
||||||
static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver);
|
static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver);
|
||||||
|
@ -17,9 +18,9 @@ static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver);
|
||||||
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||||
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||||
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
|
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
|
||||||
|
static void _finishTransfer(struct GBASIOLockstepNode* node);
|
||||||
|
|
||||||
void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
||||||
mLockstepInit(&lockstep->d);
|
|
||||||
lockstep->players[0] = 0;
|
lockstep->players[0] = 0;
|
||||||
lockstep->players[1] = 0;
|
lockstep->players[1] = 0;
|
||||||
lockstep->players[2] = 0;
|
lockstep->players[2] = 0;
|
||||||
|
@ -88,12 +89,16 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
||||||
node->nextEvent = 0;
|
node->nextEvent = 0;
|
||||||
node->eventDiff = 0;
|
node->eventDiff = 0;
|
||||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||||
|
|
||||||
|
mLockstepLock(&node->p->d);
|
||||||
|
|
||||||
node->mode = driver->p->mode;
|
node->mode = driver->p->mode;
|
||||||
|
|
||||||
switch (node->mode) {
|
switch (node->mode) {
|
||||||
case SIO_MULTI:
|
case SIO_MULTI:
|
||||||
node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister;
|
node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister;
|
||||||
node->d.p->rcnt |= 3;
|
node->d.p->rcnt |= 3;
|
||||||
++node->p->attachedMulti;
|
ATOMIC_ADD(node->p->attachedMulti, 1);
|
||||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached;
|
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached;
|
||||||
if (node->id) {
|
if (node->id) {
|
||||||
node->d.p->rcnt |= 4;
|
node->d.p->rcnt |= 4;
|
||||||
|
@ -110,35 +115,83 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
||||||
node->phase = node->p->d.transferActive;
|
node->phase = node->p->d.transferActive;
|
||||||
node->transferId = node->p->d.transferId;
|
node->transferId = node->p->d.transferId;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
||||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||||
|
|
||||||
|
mLockstepLock(&node->p->d);
|
||||||
|
|
||||||
node->mode = driver->p->mode;
|
node->mode = driver->p->mode;
|
||||||
switch (node->mode) {
|
switch (node->mode) {
|
||||||
case SIO_MULTI:
|
case SIO_MULTI:
|
||||||
--node->p->attachedMulti;
|
ATOMIC_SUB(node->p->attachedMulti, 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush ongoing transfer
|
||||||
|
if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) {
|
||||||
|
int oldWhen = node->event.when;
|
||||||
|
|
||||||
|
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
||||||
|
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||||
|
node->eventDiff -= oldWhen - node->event.when;
|
||||||
|
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
||||||
|
}
|
||||||
|
|
||||||
node->p->d.unload(&node->p->d, node->id);
|
node->p->d.unload(&node->p->d, node->id);
|
||||||
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
|
||||||
|
node->p->multiRecv[0] = 0xFFFF;
|
||||||
|
node->p->multiRecv[1] = 0xFFFF;
|
||||||
|
node->p->multiRecv[2] = 0xFFFF;
|
||||||
|
node->p->multiRecv[3] = 0xFFFF;
|
||||||
|
|
||||||
|
_finishTransfer(node);
|
||||||
|
|
||||||
|
if (!node->id) {
|
||||||
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate SIO mode
|
||||||
|
node->mode = SIO_GPIO;
|
||||||
|
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||||
|
|
||||||
|
mLockstepLock(&node->p->d);
|
||||||
|
|
||||||
if (address == REG_SIOCNT) {
|
if (address == REG_SIOCNT) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
||||||
if (value & 0x0080 && node->p->d.transferActive == TRANSFER_IDLE) {
|
|
||||||
|
enum mLockstepPhase transferActive;
|
||||||
|
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
|
||||||
|
|
||||||
|
if (value & 0x0080 && transferActive == TRANSFER_IDLE) {
|
||||||
if (!node->id && node->d.p->multiplayerControl.ready) {
|
if (!node->id && node->d.p->multiplayerControl.ready) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
||||||
node->p->d.transferActive = TRANSFER_STARTING;
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
||||||
node->p->d.transferCycles = GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1];
|
ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1]);
|
||||||
|
|
||||||
|
bool scheduled = mTimingIsScheduled(&driver->p->p->timing, &node->event);
|
||||||
|
int oldWhen = node->event.when;
|
||||||
|
|
||||||
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
mTimingDeschedule(&driver->p->p->timing, &node->event);
|
||||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||||
|
|
||||||
|
if (scheduled) {
|
||||||
|
node->eventDiff -= oldWhen - node->event.when;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
value &= ~0x0080;
|
value &= ~0x0080;
|
||||||
}
|
}
|
||||||
|
@ -148,6 +201,9 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
|
||||||
} else if (address == REG_SIOMLT_SEND) {
|
} else if (address == REG_SIOMLT_SEND) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +211,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
||||||
if (node->transferFinished) {
|
if (node->transferFinished) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBASIO* sio = node->d.p;
|
struct GBASIO* sio = node->d.p;
|
||||||
switch (node->mode) {
|
switch (node->mode) {
|
||||||
case SIO_MULTI:
|
case SIO_MULTI:
|
||||||
|
@ -209,27 +266,38 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
||||||
static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
||||||
bool needsToWait = false;
|
bool needsToWait = false;
|
||||||
int i;
|
int i;
|
||||||
switch (node->p->d.transferActive) {
|
|
||||||
|
enum mLockstepPhase transferActive;
|
||||||
|
int attachedMulti, attached;
|
||||||
|
|
||||||
|
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
|
||||||
|
ATOMIC_LOAD(attachedMulti, node->p->attachedMulti);
|
||||||
|
ATOMIC_LOAD(attached, node->p->d.attached);
|
||||||
|
|
||||||
|
switch (transferActive) {
|
||||||
case TRANSFER_IDLE:
|
case TRANSFER_IDLE:
|
||||||
// If the master hasn't initiated a transfer, it can keep going.
|
// If the master hasn't initiated a transfer, it can keep going.
|
||||||
node->nextEvent += LOCKSTEP_INCREMENT;
|
node->nextEvent += LOCKSTEP_INCREMENT;
|
||||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached;
|
node->d.p->multiplayerControl.ready = attachedMulti == attached;
|
||||||
break;
|
break;
|
||||||
case TRANSFER_STARTING:
|
case TRANSFER_STARTING:
|
||||||
// Start the transfer, but wait for the other GBAs to catch up
|
// Start the transfer, but wait for the other GBAs to catch up
|
||||||
node->transferFinished = false;
|
node->transferFinished = false;
|
||||||
node->p->multiRecv[0] = 0xFFFF;
|
node->p->multiRecv[0] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||||
|
node->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = 0xFFFF;
|
||||||
|
node->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = 0xFFFF;
|
||||||
|
node->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
|
||||||
|
node->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
|
||||||
node->p->multiRecv[1] = 0xFFFF;
|
node->p->multiRecv[1] = 0xFFFF;
|
||||||
node->p->multiRecv[2] = 0xFFFF;
|
node->p->multiRecv[2] = 0xFFFF;
|
||||||
node->p->multiRecv[3] = 0xFFFF;
|
node->p->multiRecv[3] = 0xFFFF;
|
||||||
needsToWait = true;
|
needsToWait = true;
|
||||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTED);
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTED);
|
||||||
node->nextEvent += 512;
|
node->nextEvent += LOCKSTEP_TRANSFER;
|
||||||
break;
|
break;
|
||||||
case TRANSFER_STARTED:
|
case TRANSFER_STARTED:
|
||||||
// All the other GBAs have caught up and are sleeping, we can all continue now
|
// All the other GBAs have caught up and are sleeping, we can all continue now
|
||||||
node->p->multiRecv[0] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
node->nextEvent += LOCKSTEP_TRANSFER;
|
||||||
node->nextEvent += 512;
|
|
||||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHING);
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHING);
|
||||||
break;
|
break;
|
||||||
case TRANSFER_FINISHING:
|
case TRANSFER_FINISHING:
|
||||||
|
@ -269,6 +337,7 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
node->phase = node->p->d.transferActive;
|
node->phase = node->p->d.transferActive;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (needsToWait) {
|
if (needsToWait) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -276,9 +345,16 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
||||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached;
|
enum mLockstepPhase transferActive;
|
||||||
|
int attachedMulti, attached;
|
||||||
|
|
||||||
|
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
|
||||||
|
ATOMIC_LOAD(attachedMulti, node->p->attachedMulti);
|
||||||
|
ATOMIC_LOAD(attached, node->p->d.attached);
|
||||||
|
|
||||||
|
node->d.p->multiplayerControl.ready = attachedMulti == attached;
|
||||||
bool signal = false;
|
bool signal = false;
|
||||||
switch (node->p->d.transferActive) {
|
switch (transferActive) {
|
||||||
case TRANSFER_IDLE:
|
case TRANSFER_IDLE:
|
||||||
if (!node->d.p->multiplayerControl.ready) {
|
if (!node->d.p->multiplayerControl.ready) {
|
||||||
node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT);
|
node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT);
|
||||||
|
@ -288,6 +364,9 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
||||||
case TRANSFER_FINISHING:
|
case TRANSFER_FINISHING:
|
||||||
break;
|
break;
|
||||||
case TRANSFER_STARTED:
|
case TRANSFER_STARTED:
|
||||||
|
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
node->transferFinished = false;
|
node->transferFinished = false;
|
||||||
switch (node->mode) {
|
switch (node->mode) {
|
||||||
case SIO_MULTI:
|
case SIO_MULTI:
|
||||||
|
@ -315,6 +394,9 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
||||||
signal = true;
|
signal = true;
|
||||||
break;
|
break;
|
||||||
case TRANSFER_FINISHED:
|
case TRANSFER_FINISHED:
|
||||||
|
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
_finishTransfer(node);
|
_finishTransfer(node);
|
||||||
signal = true;
|
signal = true;
|
||||||
break;
|
break;
|
||||||
|
@ -325,16 +407,20 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
||||||
if (signal) {
|
if (signal) {
|
||||||
node->p->d.signal(&node->p->d, 1 << node->id);
|
node->p->d.signal(&node->p->d, 1 << node->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
struct GBASIOLockstepNode* node = user;
|
struct GBASIOLockstepNode* node = user;
|
||||||
|
mLockstepLock(&node->p->d);
|
||||||
if (node->p->d.attached < 2) {
|
if (node->p->d.attached < 2) {
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int32_t cycles = 0;
|
int32_t cycles = 0;
|
||||||
node->nextEvent -= cyclesLate;
|
node->nextEvent -= cyclesLate;
|
||||||
|
node->eventDiff += cyclesLate;
|
||||||
if (node->nextEvent <= 0) {
|
if (node->nextEvent <= 0) {
|
||||||
if (!node->id) {
|
if (!node->id) {
|
||||||
cycles = _masterUpdate(node);
|
cycles = _masterUpdate(node);
|
||||||
|
@ -353,12 +439,18 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
|
||||||
mTimingSchedule(timing, &node->event, cycles);
|
mTimingSchedule(timing, &node->event, cycles);
|
||||||
} else {
|
} else {
|
||||||
node->d.p->p->earlyExit = true;
|
node->d.p->p->earlyExit = true;
|
||||||
mTimingSchedule(timing, &node->event, cyclesLate + 1);
|
node->eventDiff += 1;
|
||||||
|
mTimingSchedule(timing, &node->event, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||||
|
|
||||||
|
mLockstepLock(&node->p->d);
|
||||||
|
|
||||||
if (address == REG_SIOCNT) {
|
if (address == REG_SIOCNT) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
||||||
value &= 0xFF8B;
|
value &= 0xFF8B;
|
||||||
|
@ -368,7 +460,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
||||||
if (value & 0x0080 && !node->id) {
|
if (value & 0x0080 && !node->id) {
|
||||||
// Internal shift clock
|
// Internal shift clock
|
||||||
if (value & 1) {
|
if (value & 1) {
|
||||||
node->p->d.transferActive = TRANSFER_STARTING;
|
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
||||||
}
|
}
|
||||||
// Frequency
|
// Frequency
|
||||||
if (value & 2) {
|
if (value & 2) {
|
||||||
|
@ -382,5 +474,8 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
||||||
} else if (address == REG_SIODATA32_HI) {
|
} else if (address == REG_SIODATA32_HI) {
|
||||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value);
|
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLockstepUnlock(&node->p->d);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
if(DEFINED ENV{DEVKITPRO})
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(DEFINED ENV{DEVKITARM})
|
|
||||||
set(DEVKITARM $ENV{DEVKITARM})
|
|
||||||
else()
|
|
||||||
set(DEVKITARM ${DEVKITPRO}/devkitARM)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(DEFINED ENV{CTRULIB})
|
if(DEFINED ENV{CTRULIB})
|
||||||
set(CTRULIB $ENV{CTRULIB})
|
set(CTRULIB $ENV{CTRULIB})
|
||||||
|
@ -16,40 +6,17 @@ else()
|
||||||
set(CTRULIB ${DEVKITPRO}/libctru)
|
set(CTRULIB ${DEVKITPRO}/libctru)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(extension)
|
|
||||||
if (CMAKE_HOST_WIN32)
|
|
||||||
set(extension .exe)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_PROGRAM_PATH ${DEVKITARM}/bin)
|
|
||||||
set(cross_prefix arm-none-eabi-)
|
set(cross_prefix arm-none-eabi-)
|
||||||
set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections")
|
set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections")
|
||||||
set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations")
|
set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations")
|
||||||
set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags} -Wl,--gc-sections")
|
set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags} -Wl,--gc-sections")
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor")
|
set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor")
|
||||||
set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi")
|
set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi")
|
||||||
|
|
||||||
find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension})
|
|
||||||
find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension})
|
|
||||||
find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension})
|
|
||||||
find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_LINKER ${cross_prefix}ld${extension})
|
|
||||||
set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags")
|
|
||||||
set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags")
|
|
||||||
set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags")
|
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags")
|
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")
|
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags")
|
|
||||||
|
|
||||||
set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/arm-none-eabi ${DEVKITPRO}/portlibs/3ds)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")
|
|
||||||
set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE)
|
|
||||||
|
|
||||||
set(3DS ON)
|
set(3DS ON)
|
||||||
add_definitions(-D_3DS -DARM11)
|
add_definitions(-D_3DS -DARM11)
|
||||||
|
|
||||||
|
create_devkit(ARM)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/${CMAKE_LIBRARY_ARCHITECTURE} ${DEVKITPRO}/portlibs/3ds)
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
if(DEFINED ENV{DEVKITPRO})
|
||||||
|
set(DEVKITPRO $ENV{DEVKITPRO})
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
||||||
|
|
||||||
|
function(create_devkit DEVKIT)
|
||||||
|
if(DEFINED ENV{DEVKIT${DEVKIT}})
|
||||||
|
set(DEVKIT${DEVKIT} $ENV{DEVKIT${DEVKIT}} PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(DEVKIT${DEVKIT} ${DEVKITPRO}/devkit${DEVKIT} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_PROGRAM_PATH ${DEVKIT${DEVKIT}}/bin CACHE INTERNAL "program path")
|
||||||
|
|
||||||
|
set(extension)
|
||||||
|
if (CMAKE_HOST_WIN32)
|
||||||
|
set(extension .exe)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension})
|
||||||
|
find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension})
|
||||||
|
find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
|
||||||
|
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension})
|
||||||
|
find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension})
|
||||||
|
find_program(CMAKE_LINKER ${cross_prefix}ld${extension})
|
||||||
|
set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags")
|
||||||
|
set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags")
|
||||||
|
set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags")
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags")
|
||||||
|
set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags")
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "")
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")
|
||||||
|
set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE)
|
||||||
|
endfunction()
|
|
@ -30,8 +30,11 @@ void ActionMapper::clearMenu(const QString& name) {
|
||||||
emit menuCleared(name);
|
emit menuCleared(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shortcuts) {
|
void ActionMapper::rebuildMenu(QMenuBar* menubar, QWidget* context, const ShortcutController& shortcuts) {
|
||||||
menubar->clear();
|
menubar->clear();
|
||||||
|
for (QAction* action : context->actions()) {
|
||||||
|
context->removeAction(action);
|
||||||
|
}
|
||||||
for (const QString& m : m_menus[{}]) {
|
for (const QString& m : m_menus[{}]) {
|
||||||
if (m_hiddenActions.contains(m)) {
|
if (m_hiddenActions.contains(m)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -39,11 +42,11 @@ void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shor
|
||||||
QString menu = m.mid(1);
|
QString menu = m.mid(1);
|
||||||
QMenu* qmenu = menubar->addMenu(m_menuNames[menu]);
|
QMenu* qmenu = menubar->addMenu(m_menuNames[menu]);
|
||||||
|
|
||||||
rebuildMenu(menu, qmenu, shortcuts);
|
rebuildMenu(menu, qmenu, context, shortcuts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController& shortcuts) {
|
void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, QWidget* context, const ShortcutController& shortcuts) {
|
||||||
for (const QString& actionName : m_menus[menu]) {
|
for (const QString& actionName : m_menus[menu]) {
|
||||||
if (actionName.isNull()) {
|
if (actionName.isNull()) {
|
||||||
qmenu->addSeparator();
|
qmenu->addSeparator();
|
||||||
|
@ -55,12 +58,13 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const Shortcut
|
||||||
if (actionName[0] == '.') {
|
if (actionName[0] == '.') {
|
||||||
QString name = actionName.mid(1);
|
QString name = actionName.mid(1);
|
||||||
QMenu* newMenu = qmenu->addMenu(m_menuNames[name]);
|
QMenu* newMenu = qmenu->addMenu(m_menuNames[name]);
|
||||||
rebuildMenu(name, newMenu, shortcuts);
|
rebuildMenu(name, newMenu, context, shortcuts);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Action* action = &m_actions[actionName];
|
Action* action = &m_actions[actionName];
|
||||||
QAction* qaction = qmenu->addAction(action->visibleName());
|
QAction* qaction = qmenu->addAction(action->visibleName());
|
||||||
qaction->setEnabled(action->isEnabled());
|
qaction->setEnabled(action->isEnabled());
|
||||||
|
qaction->setShortcutContext(Qt::WidgetShortcut);
|
||||||
if (action->isExclusive() || action->booleanAction()) {
|
if (action->isExclusive() || action->booleanAction()) {
|
||||||
qaction->setCheckable(true);
|
qaction->setCheckable(true);
|
||||||
}
|
}
|
||||||
|
@ -88,6 +92,7 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const Shortcut
|
||||||
qaction->setShortcut(QKeySequence(shortcut));
|
qaction->setShortcut(QKeySequence(shortcut));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
context->addAction(qaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
void addMenu(const QString& visibleName, const QString& name, const QString& parent = {});
|
void addMenu(const QString& visibleName, const QString& name, const QString& parent = {});
|
||||||
void addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent = {});
|
void addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent = {});
|
||||||
void clearMenu(const QString& name);
|
void clearMenu(const QString& name);
|
||||||
void rebuildMenu(QMenuBar*, const ShortcutController&);
|
void rebuildMenu(QMenuBar*, QWidget* context, const ShortcutController&);
|
||||||
|
|
||||||
void addSeparator(const QString& menu);
|
void addSeparator(const QString& menu);
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ signals:
|
||||||
void menuCleared(const QString& name);
|
void menuCleared(const QString& name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rebuildMenu(const QString& menu, QMenu* qmenu, const ShortcutController&);
|
void rebuildMenu(const QString& menu, QMenu* qmenu, QWidget* context, const ShortcutController&);
|
||||||
Action* addAction(const Action& act, const QString& name, const QString& menu, const QKeySequence& shortcut);
|
Action* addAction(const Action& act, const QString& name, const QString& menu, const QKeySequence& shortcut);
|
||||||
|
|
||||||
QHash<QString, Action> m_actions;
|
QHash<QString, Action> m_actions;
|
||||||
|
|
|
@ -37,6 +37,7 @@ DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
|
||||||
m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl);
|
m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl);
|
||||||
m_gl->setMouseTracking(true);
|
m_gl->setMouseTracking(true);
|
||||||
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
||||||
|
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayGL::~DisplayGL() {
|
DisplayGL::~DisplayGL() {
|
||||||
|
@ -223,6 +224,9 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
|
||||||
#endif
|
#endif
|
||||||
m_backend->swap = [](VideoBackend* v) {
|
m_backend->swap = [](VideoBackend* v) {
|
||||||
PainterGL* painter = static_cast<PainterGL*>(v->user);
|
PainterGL* painter = static_cast<PainterGL*>(v->user);
|
||||||
|
if (!painter->m_gl->isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
painter->m_gl->swapBuffers();
|
painter->m_gl->swapBuffers();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
EmptyGLWidget(const QGLFormat& format, QWidget* parent) : QGLWidget(format, parent) { setAutoBufferSwap(false); }
|
EmptyGLWidget(const QGLFormat& format, QWidget* parent) : QGLWidget(format, parent) { setAutoBufferSwap(false); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent*) override {}
|
void paintEvent(QPaintEvent* event) override { event->ignore(); }
|
||||||
void resizeEvent(QResizeEvent*) override {}
|
void resizeEvent(QResizeEvent*) override {}
|
||||||
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,7 +79,9 @@ void MessagePainter::redraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessagePainter::paint(QPainter* painter) {
|
void MessagePainter::paint(QPainter* painter) {
|
||||||
painter->drawPixmap(m_local, m_pixmap);
|
if (!m_message.text().isEmpty()) {
|
||||||
|
painter->drawPixmap(m_local, m_pixmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,26 +16,43 @@
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
|
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
|
||||||
|
: controller(coreController)
|
||||||
|
, gbNode(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
|
||||||
|
: controller(coreController)
|
||||||
|
, gbaNode(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
MultiplayerController::MultiplayerController() {
|
MultiplayerController::MultiplayerController() {
|
||||||
mLockstepInit(&m_lockstep);
|
mLockstepInit(&m_lockstep);
|
||||||
m_lockstep.context = this;
|
m_lockstep.context = this;
|
||||||
|
m_lockstep.lock = [](mLockstep* lockstep) {
|
||||||
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
|
controller->m_lock.lock();
|
||||||
|
};
|
||||||
|
m_lockstep.unlock = [](mLockstep* lockstep) {
|
||||||
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
|
controller->m_lock.unlock();
|
||||||
|
};
|
||||||
m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) {
|
m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
Player* player = &controller->m_players[0];
|
Player* player = &controller->m_players[0];
|
||||||
bool woke = false;
|
bool woke = false;
|
||||||
controller->m_lock.lock();
|
|
||||||
player->waitMask &= ~mask;
|
player->waitMask &= ~mask;
|
||||||
if (!player->waitMask && player->awake < 1) {
|
if (!player->waitMask && player->awake < 1) {
|
||||||
mCoreThreadStopWaiting(player->controller->thread());
|
mCoreThreadStopWaiting(player->controller->thread());
|
||||||
player->awake = 1;
|
player->awake = 1;
|
||||||
woke = true;
|
woke = true;
|
||||||
}
|
}
|
||||||
controller->m_lock.unlock();
|
|
||||||
return woke;
|
return woke;
|
||||||
};
|
};
|
||||||
m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
|
m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
controller->m_lock.lock();
|
|
||||||
Player* player = &controller->m_players[0];
|
Player* player = &controller->m_players[0];
|
||||||
bool slept = false;
|
bool slept = false;
|
||||||
player->waitMask |= mask;
|
player->waitMask |= mask;
|
||||||
|
@ -44,7 +61,6 @@ MultiplayerController::MultiplayerController() {
|
||||||
player->awake = 0;
|
player->awake = 0;
|
||||||
slept = true;
|
slept = true;
|
||||||
}
|
}
|
||||||
controller->m_lock.unlock();
|
|
||||||
return slept;
|
return slept;
|
||||||
};
|
};
|
||||||
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||||
|
@ -52,7 +68,6 @@ MultiplayerController::MultiplayerController() {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
controller->m_lock.lock();
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
for (int i = 1; i < controller->m_players.count(); ++i) {
|
for (int i = 1; i < controller->m_players.count(); ++i) {
|
||||||
Player* player = &controller->m_players[i];
|
Player* player = &controller->m_players[i];
|
||||||
|
@ -85,11 +100,9 @@ MultiplayerController::MultiplayerController() {
|
||||||
controller->m_players[id].controller->setSync(true);
|
controller->m_players[id].controller->setSync(true);
|
||||||
controller->m_players[id].cyclesPosted += cycles;
|
controller->m_players[id].cyclesPosted += cycles;
|
||||||
}
|
}
|
||||||
controller->m_lock.unlock();
|
|
||||||
};
|
};
|
||||||
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
controller->m_lock.lock();
|
|
||||||
Player* player = &controller->m_players[id];
|
Player* player = &controller->m_players[id];
|
||||||
player->cyclesPosted -= cycles;
|
player->cyclesPosted -= cycles;
|
||||||
if (player->cyclesPosted <= 0) {
|
if (player->cyclesPosted <= 0) {
|
||||||
|
@ -97,15 +110,23 @@ MultiplayerController::MultiplayerController() {
|
||||||
player->awake = 0;
|
player->awake = 0;
|
||||||
}
|
}
|
||||||
cycles = player->cyclesPosted;
|
cycles = player->cyclesPosted;
|
||||||
controller->m_lock.unlock();
|
return cycles;
|
||||||
|
};
|
||||||
|
m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
|
||||||
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
|
Player* player = &controller->m_players[id];
|
||||||
|
auto cycles = player->cyclesPosted;
|
||||||
return cycles;
|
return cycles;
|
||||||
};
|
};
|
||||||
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
controller->m_lock.lock();
|
|
||||||
Player* player = &controller->m_players[id];
|
|
||||||
if (id) {
|
if (id) {
|
||||||
|
Player* player = &controller->m_players[id];
|
||||||
player->controller->setSync(true);
|
player->controller->setSync(true);
|
||||||
|
player->cyclesPosted = 0;
|
||||||
|
|
||||||
|
// release master GBA if it is waiting for this GBA
|
||||||
|
player = &controller->m_players[0];
|
||||||
player->waitMask &= ~(1 << id);
|
player->waitMask &= ~(1 << id);
|
||||||
if (!player->waitMask && player->awake < 1) {
|
if (!player->waitMask && player->awake < 1) {
|
||||||
mCoreThreadStopWaiting(player->controller->thread());
|
mCoreThreadStopWaiting(player->controller->thread());
|
||||||
|
@ -149,10 +170,13 @@ MultiplayerController::MultiplayerController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller->m_lock.unlock();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiplayerController::~MultiplayerController() {
|
||||||
|
mLockstepDeinit(&m_lockstep);
|
||||||
|
}
|
||||||
|
|
||||||
bool MultiplayerController::attachGame(CoreController* controller) {
|
bool MultiplayerController::attachGame(CoreController* controller) {
|
||||||
if (m_lockstep.attached == MAX_GBAS) {
|
if (m_lockstep.attached == MAX_GBAS) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -188,14 +212,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
|
||||||
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
||||||
GBASIOLockstepNodeCreate(node);
|
GBASIOLockstepNodeCreate(node);
|
||||||
GBASIOLockstepAttachNode(&m_gbaLockstep, node);
|
GBASIOLockstepAttachNode(&m_gbaLockstep, node);
|
||||||
m_players.append({
|
m_players.append({controller, node});
|
||||||
controller,
|
|
||||||
nullptr,
|
|
||||||
node,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
});
|
|
||||||
|
|
||||||
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
|
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
|
||||||
|
|
||||||
|
@ -210,14 +227,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
|
||||||
GBSIOLockstepNode* node = new GBSIOLockstepNode;
|
GBSIOLockstepNode* node = new GBSIOLockstepNode;
|
||||||
GBSIOLockstepNodeCreate(node);
|
GBSIOLockstepNodeCreate(node);
|
||||||
GBSIOLockstepAttachNode(&m_gbLockstep, node);
|
GBSIOLockstepAttachNode(&m_gbLockstep, node);
|
||||||
m_players.append({
|
m_players.append({controller, node});
|
||||||
controller,
|
|
||||||
node,
|
|
||||||
nullptr,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
});
|
|
||||||
|
|
||||||
GBSIOSetDriver(&gb->sio, &node->d);
|
GBSIOSetDriver(&gb->sio, &node->d);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <mgba/internal/gb/sio/lockstep.h>
|
#include <mgba/internal/gb/sio/lockstep.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct GBSIOLockstepNode;
|
struct GBSIOLockstepNode;
|
||||||
struct GBASIOLockstepNode;
|
struct GBASIOLockstepNode;
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MultiplayerController();
|
MultiplayerController();
|
||||||
|
~MultiplayerController();
|
||||||
|
|
||||||
bool attachGame(CoreController*);
|
bool attachGame(CoreController*);
|
||||||
void detachGame(CoreController*);
|
void detachGame(CoreController*);
|
||||||
|
@ -42,12 +45,15 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Player {
|
struct Player {
|
||||||
|
Player(CoreController* controller, GBSIOLockstepNode* node);
|
||||||
|
Player(CoreController* controller, GBASIOLockstepNode* node);
|
||||||
|
|
||||||
CoreController* controller;
|
CoreController* controller;
|
||||||
GBSIOLockstepNode* gbNode;
|
GBSIOLockstepNode* gbNode = nullptr;
|
||||||
GBASIOLockstepNode* gbaNode;
|
GBASIOLockstepNode* gbaNode = nullptr;
|
||||||
int awake;
|
int awake = 1;
|
||||||
int32_t cyclesPosted;
|
int32_t cyclesPosted = 0;
|
||||||
unsigned waitMask;
|
unsigned waitMask = 0;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
mLockstep m_lockstep;
|
mLockstep m_lockstep;
|
||||||
|
|
|
@ -279,7 +279,11 @@ void VideoView::setAudioCodec(const QString& codec, bool manual) {
|
||||||
void VideoView::setVideoCodec(const QString& codec, bool manual) {
|
void VideoView::setVideoCodec(const QString& codec, bool manual) {
|
||||||
free(m_videoCodecCstr);
|
free(m_videoCodecCstr);
|
||||||
m_videoCodec = sanitizeCodec(codec, s_vcodecMap);
|
m_videoCodec = sanitizeCodec(codec, s_vcodecMap);
|
||||||
m_videoCodecCstr = strdup(m_videoCodec.toUtf8().constData());
|
if (m_videoCodec == "none") {
|
||||||
|
m_videoCodecCstr = nullptr;
|
||||||
|
} else {
|
||||||
|
m_videoCodecCstr = strdup(m_videoCodec.toUtf8().constData());
|
||||||
|
}
|
||||||
if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) {
|
if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) {
|
||||||
free(m_videoCodecCstr);
|
free(m_videoCodecCstr);
|
||||||
m_videoCodecCstr = nullptr;
|
m_videoCodecCstr = nullptr;
|
||||||
|
|
|
@ -608,9 +608,7 @@ void Window::resizeEvent(QResizeEvent* event) {
|
||||||
}
|
}
|
||||||
m_savedScale = factor;
|
m_savedScale = factor;
|
||||||
for (QMap<int, Action*>::iterator iter = m_frameSizes.begin(); iter != m_frameSizes.end(); ++iter) {
|
for (QMap<int, Action*>::iterator iter = m_frameSizes.begin(); iter != m_frameSizes.end(); ++iter) {
|
||||||
bool enableSignals = iter.value()->blockSignals(true);
|
|
||||||
iter.value()->setActive(iter.key() == factor);
|
iter.value()->setActive(iter.key() == factor);
|
||||||
iter.value()->blockSignals(enableSignals);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_config->setOption("fullscreen", isFullScreen());
|
m_config->setOption("fullscreen", isFullScreen());
|
||||||
|
@ -636,6 +634,7 @@ void Window::showEvent(QShowEvent* event) {
|
||||||
m_fullscreenOnStart = false;
|
m_fullscreenOnStart = false;
|
||||||
}
|
}
|
||||||
reloadDisplayDriver();
|
reloadDisplayDriver();
|
||||||
|
setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::closeEvent(QCloseEvent* event) {
|
void Window::closeEvent(QCloseEvent* event) {
|
||||||
|
@ -835,7 +834,7 @@ void Window::gameStarted() {
|
||||||
action->setActive(true);
|
action->setActive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_actions.rebuildMenu(menuBar(), *m_shortcutController);
|
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_DISCORD_RPC
|
#ifdef USE_DISCORD_RPC
|
||||||
|
@ -1382,9 +1381,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
m_savedScale = i;
|
m_savedScale = i;
|
||||||
m_config->setOption("scaleMultiplier", i); // TODO: Port to other
|
m_config->setOption("scaleMultiplier", i); // TODO: Port to other
|
||||||
resizeFrame(size);
|
resizeFrame(size);
|
||||||
bool enableSignals = setSize->blockSignals(true);
|
|
||||||
setSize->setActive(true);
|
setSize->setActive(true);
|
||||||
setSize->blockSignals(enableSignals);
|
|
||||||
}, "frame");
|
}, "frame");
|
||||||
setSize->setExclusive(true);
|
setSize->setExclusive(true);
|
||||||
if (m_savedScale == i) {
|
if (m_savedScale == i) {
|
||||||
|
@ -1481,18 +1478,13 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
addGameAction(tr("Record output..."), "recordOutput", this, &Window::openVideoWindow, "av");
|
addGameAction(tr("Record A/V..."), "recordOutput", this, &Window::openVideoWindow, "av");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MAGICK
|
#ifdef USE_MAGICK
|
||||||
addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av");
|
addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
addGameAction(tr("Record video log..."), "recordVL", this, &Window::startVideoLog, "av");
|
|
||||||
addGameAction(tr("Stop video log"), "stopVL", [this]() {
|
|
||||||
m_controller->endVideoLog();
|
|
||||||
}, "av");
|
|
||||||
|
|
||||||
m_actions.addSeparator("av");
|
m_actions.addSeparator("av");
|
||||||
m_actions.addMenu(tr("Video layers"), "videoLayers", "av");
|
m_actions.addMenu(tr("Video layers"), "videoLayers", "av");
|
||||||
m_actions.addMenu(tr("Audio channels"), "audioChannels", "av");
|
m_actions.addMenu(tr("Audio channels"), "audioChannels", "av");
|
||||||
|
@ -1552,6 +1544,12 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
m_platformActions.insert(PLATFORM_GBA, ioViewer);
|
m_platformActions.insert(PLATFORM_GBA, ioViewer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_actions.addSeparator("tools");
|
||||||
|
addGameAction(tr("Record debug video log..."), "recordVL", this, &Window::startVideoLog, "tools");
|
||||||
|
addGameAction(tr("Stop debug video log"), "stopVL", [this]() {
|
||||||
|
m_controller->endVideoLog();
|
||||||
|
}, "tools");
|
||||||
|
|
||||||
ConfigOption* skipBios = m_config->addOption("skipBios");
|
ConfigOption* skipBios = m_config->addOption("skipBios");
|
||||||
skipBios->connect([this](const QVariant& value) {
|
skipBios->connect([this](const QVariant& value) {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
|
@ -1642,7 +1640,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_shortcutController->rebuildItems();
|
m_shortcutController->rebuildItems();
|
||||||
m_actions.rebuildMenu(menubar, *m_shortcutController);
|
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::attachWidget(QWidget* widget) {
|
void Window::attachWidget(QWidget* widget) {
|
||||||
|
@ -1679,7 +1677,7 @@ void Window::updateMRU() {
|
||||||
}
|
}
|
||||||
m_config->setMRU(m_mruFiles);
|
m_config->setMRU(m_mruFiles);
|
||||||
m_config->write();
|
m_config->write();
|
||||||
m_actions.rebuildMenu(menuBar(), *m_shortcutController);
|
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||||
}
|
}
|
||||||
|
|
||||||
Action* Window::addGameAction(const QString& visibleName, const QString& name, Action::Function function, const QString& menu, const QKeySequence& shortcut) {
|
Action* Window::addGameAction(const QString& visibleName, const QString& name, Action::Function function, const QString& menu, const QKeySequence& shortcut) {
|
||||||
|
|
|
@ -60,7 +60,7 @@ set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/main.c)
|
||||||
|
|
||||||
if(BUILD_RASPI)
|
if(BUILD_RASPI)
|
||||||
add_definitions(-DBUILD_RASPI)
|
add_definitions(-DBUILD_RASPI)
|
||||||
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c)
|
list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-common.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/rpi-common.c)
|
||||||
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c)
|
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gles2-sdl.c)
|
||||||
set(OPENGLES2_LIBRARY "-lEGL -lGLESv2 -lbcm_host")
|
set(OPENGLES2_LIBRARY "-lEGL -lGLESv2 -lbcm_host")
|
||||||
set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE)
|
set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE)
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
#include "gl-common.h"
|
#include "gl-common.h"
|
||||||
|
#ifdef BUILD_RASPI
|
||||||
|
#include "rpi-common.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/thread.h>
|
#include <mgba/core/thread.h>
|
||||||
|
@ -26,74 +29,7 @@ void mSDLGLES2Create(struct mSDLRenderer* renderer) {
|
||||||
|
|
||||||
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
#ifdef BUILD_RASPI
|
#ifdef BUILD_RASPI
|
||||||
bcm_host_init();
|
mRPIGLCommonInit(renderer);
|
||||||
renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
||||||
int major, minor;
|
|
||||||
if (EGL_FALSE == eglInitialize(renderer->display, &major, &minor)) {
|
|
||||||
printf("Failed to initialize EGL");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) {
|
|
||||||
printf("Failed to get GLES API");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EGLint requestConfig[] = {
|
|
||||||
EGL_RED_SIZE, 5,
|
|
||||||
EGL_GREEN_SIZE, 5,
|
|
||||||
EGL_BLUE_SIZE, 5,
|
|
||||||
EGL_ALPHA_SIZE, 1,
|
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
||||||
EGL_NONE
|
|
||||||
};
|
|
||||||
|
|
||||||
EGLConfig config;
|
|
||||||
EGLint numConfigs;
|
|
||||||
|
|
||||||
if (EGL_FALSE == eglChooseConfig(renderer->display, requestConfig, &config, 1, &numConfigs)) {
|
|
||||||
printf("Failed to choose EGL config\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EGLint contextAttributes[] = {
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
||||||
EGL_NONE
|
|
||||||
};
|
|
||||||
|
|
||||||
int dispWidth = 240, dispHeight = 160, adjWidth;
|
|
||||||
renderer->context = eglCreateContext(renderer->display, config, EGL_NO_CONTEXT, contextAttributes);
|
|
||||||
graphics_get_display_size(0, &dispWidth, &dispHeight);
|
|
||||||
adjWidth = dispHeight / 2 * 3;
|
|
||||||
|
|
||||||
DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
|
|
||||||
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
|
|
||||||
|
|
||||||
VC_RECT_T destRect = {
|
|
||||||
.x = (dispWidth - adjWidth) / 2,
|
|
||||||
.y = 0,
|
|
||||||
.width = adjWidth,
|
|
||||||
.height = dispHeight
|
|
||||||
};
|
|
||||||
|
|
||||||
VC_RECT_T srcRect = {
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.width = 240 << 16,
|
|
||||||
.height = 160 << 16
|
|
||||||
};
|
|
||||||
|
|
||||||
DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0);
|
|
||||||
vc_dispmanx_update_submit_sync(update);
|
|
||||||
|
|
||||||
renderer->window.element = element;
|
|
||||||
renderer->window.width = dispWidth;
|
|
||||||
renderer->window.height = dispHeight;
|
|
||||||
|
|
||||||
renderer->surface = eglCreateWindowSurface(renderer->display, config, &renderer->window, 0);
|
|
||||||
if (EGL_FALSE == eglMakeCurrent(renderer->display, renderer->surface, renderer->surface, renderer->context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
mSDLGLCommonInit(renderer);
|
mSDLGLCommonInit(renderer);
|
||||||
#endif
|
#endif
|
||||||
|
@ -112,7 +48,11 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
renderer->gl2.d.lockAspectRatio = renderer->lockAspectRatio;
|
renderer->gl2.d.lockAspectRatio = renderer->lockAspectRatio;
|
||||||
renderer->gl2.d.lockIntegerScaling = renderer->lockIntegerScaling;
|
renderer->gl2.d.lockIntegerScaling = renderer->lockIntegerScaling;
|
||||||
renderer->gl2.d.filter = renderer->filter;
|
renderer->gl2.d.filter = renderer->filter;
|
||||||
|
#ifdef BUILD_RASPI
|
||||||
|
renderer->gl2.d.swap = mRPIGLCommonSwap;
|
||||||
|
#else
|
||||||
renderer->gl2.d.swap = mSDLGLCommonSwap;
|
renderer->gl2.d.swap = mSDLGLCommonSwap;
|
||||||
|
#endif
|
||||||
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
||||||
renderer->gl2.d.setDimensions(&renderer->gl2.d, renderer->width, renderer->height);
|
renderer->gl2.d.setDimensions(&renderer->gl2.d, renderer->width, renderer->height);
|
||||||
|
|
||||||
|
@ -147,11 +87,7 @@ void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
}
|
}
|
||||||
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||||
v->drawFrame(v);
|
v->drawFrame(v);
|
||||||
#ifdef BUILD_RASPI
|
|
||||||
eglSwapBuffers(renderer->display, renderer->surface);
|
|
||||||
#else
|
|
||||||
v->swap(v);
|
v->swap(v);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,10 +96,10 @@ void mSDLGLES2Deinit(struct mSDLRenderer* renderer) {
|
||||||
renderer->gl2.d.deinit(&renderer->gl2.d);
|
renderer->gl2.d.deinit(&renderer->gl2.d);
|
||||||
}
|
}
|
||||||
#ifdef BUILD_RASPI
|
#ifdef BUILD_RASPI
|
||||||
eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
eglMakeCurrent(renderer->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
eglDestroySurface(renderer->display, renderer->surface);
|
eglDestroySurface(renderer->eglDisplay, renderer->eglSurface);
|
||||||
eglDestroyContext(renderer->display, renderer->context);
|
eglDestroyContext(renderer->eglDisplay, renderer->eglContext);
|
||||||
eglTerminate(renderer->display);
|
eglTerminate(renderer->eglDisplay);
|
||||||
bcm_host_deinit();
|
bcm_host_deinit();
|
||||||
#elif SDL_VERSION_ATLEAST(2, 0, 0)
|
#elif SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_GL_DeleteContext(renderer->glCtx);
|
SDL_GL_DeleteContext(renderer->glCtx);
|
||||||
|
|
|
@ -81,10 +81,10 @@ struct mSDLRenderer {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUILD_RASPI
|
#ifdef BUILD_RASPI
|
||||||
EGLDisplay display;
|
EGLDisplay eglDisplay;
|
||||||
EGLSurface surface;
|
EGLSurface eglSurface;
|
||||||
EGLContext context;
|
EGLContext eglContext;
|
||||||
EGL_DISPMANX_WINDOW_T window;
|
EGL_DISPMANX_WINDOW_T eglWindow;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUILD_PANDORA
|
#ifdef BUILD_PANDORA
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include <mgba/core/version.h>
|
||||||
|
|
||||||
|
void mRPIGLCommonSwap(struct VideoBackend* context) {
|
||||||
|
struct mSDLRenderer* renderer = (struct mSDLRenderer*) context->user;
|
||||||
|
eglSwapBuffers(renderer->eglDisplay, renderer->eglSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mRPIGLCommonInit(struct mSDLRenderer* renderer) {
|
||||||
|
bcm_host_init();
|
||||||
|
renderer->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
int major, minor;
|
||||||
|
if (EGL_FALSE == eglInitialize(renderer->eglDisplay, &major, &minor)) {
|
||||||
|
printf("Failed to initialize EGL");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EGL_FALSE == eglBindAPI(EGL_OPENGL_ES_API)) {
|
||||||
|
printf("Failed to get GLES API");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EGLint requestConfig[] = {
|
||||||
|
EGL_RED_SIZE, 5,
|
||||||
|
EGL_GREEN_SIZE, 5,
|
||||||
|
EGL_BLUE_SIZE, 5,
|
||||||
|
EGL_ALPHA_SIZE, 1,
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
EGLConfig config;
|
||||||
|
EGLint numConfigs;
|
||||||
|
|
||||||
|
if (EGL_FALSE == eglChooseConfig(renderer->eglDisplay, requestConfig, &config, 1, &numConfigs)) {
|
||||||
|
printf("Failed to choose EGL config\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EGLint contextAttributes[] = {
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
int dispWidth = 240, dispHeight = 160, adjWidth;
|
||||||
|
renderer->eglContext = eglCreateContext(renderer->eglDisplay, config, EGL_NO_CONTEXT, contextAttributes);
|
||||||
|
graphics_get_display_size(0, &dispWidth, &dispHeight);
|
||||||
|
adjWidth = dispHeight / 2 * 3;
|
||||||
|
|
||||||
|
DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
|
||||||
|
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
|
||||||
|
|
||||||
|
VC_RECT_T destRect = {
|
||||||
|
.x = (dispWidth - adjWidth) / 2,
|
||||||
|
.y = 0,
|
||||||
|
.width = adjWidth,
|
||||||
|
.height = dispHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
VC_RECT_T srcRect = {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = 240 << 16,
|
||||||
|
.height = 160 << 16
|
||||||
|
};
|
||||||
|
|
||||||
|
DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(update, display, 0, &destRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, 0);
|
||||||
|
vc_dispmanx_update_submit_sync(update);
|
||||||
|
|
||||||
|
renderer->eglWindow.element = element;
|
||||||
|
renderer->eglWindow.width = dispWidth;
|
||||||
|
renderer->eglWindow.height = dispHeight;
|
||||||
|
|
||||||
|
renderer->eglSurface = eglCreateWindowSurface(renderer->eglDisplay, config, &renderer->eglWindow, 0);
|
||||||
|
if (EGL_FALSE == eglMakeCurrent(renderer->eglDisplay, renderer->eglSurface, renderer->eglSurface, renderer->eglContext)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef SDL_RPI_COMMON_H
|
||||||
|
#define SDL_RPI_COMMON_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
void mRPIGLCommonSwap(struct VideoBackend* context);
|
||||||
|
void mRPIGLCommonInit(struct mSDLRenderer* renderer);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,14 +1,4 @@
|
||||||
if(DEFINED ENV{DEVKITPRO})
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(DEFINED ENV{DEVKITA64})
|
|
||||||
set(DEVKITA64 $ENV{DEVKITA64})
|
|
||||||
else()
|
|
||||||
set(DEVKITA64 ${DEVKITPRO}/devkitA64)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(DEFINED ENV{LIBNX})
|
if(DEFINED ENV{LIBNX})
|
||||||
set(LIBNX $ENV{LIBNX})
|
set(LIBNX $ENV{LIBNX})
|
||||||
|
@ -16,41 +6,17 @@ else()
|
||||||
set(LIBNX ${DEVKITPRO}/libnx)
|
set(LIBNX ${DEVKITPRO}/libnx)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(extension)
|
|
||||||
if (CMAKE_HOST_WIN32)
|
|
||||||
set(extension .exe)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_PROGRAM_PATH ${DEVKITA64}/bin)
|
|
||||||
set(cross_prefix aarch64-none-elf-)
|
set(cross_prefix aarch64-none-elf-)
|
||||||
set(arch_flags "-mtune=cortex-a57 -ffunction-sections -march=armv8-a -mtp=soft -fPIC -ftls-model=local-exec")
|
set(arch_flags "-mtune=cortex-a57 -ffunction-sections -march=armv8-a -mtp=soft -fPIC -ftls-model=local-exec")
|
||||||
set(inc_flags "-I${LIBNX}/include ${arch_flags}")
|
set(inc_flags "-I${LIBNX}/include ${arch_flags}")
|
||||||
set(link_flags "-L${LIBNX}/lib -lnx -specs=${LIBNX}/switch.specs ${arch_flags}")
|
set(link_flags "-L${LIBNX}/lib -lnx -specs=${LIBNX}/switch.specs ${arch_flags}")
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR aarch64 CACHE INTERNAL "processor")
|
set(CMAKE_SYSTEM_PROCESSOR aarch64 CACHE INTERNAL "processor")
|
||||||
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-none-elf CACHE INTERNAL "abi")
|
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-none-elf CACHE INTERNAL "abi")
|
||||||
|
|
||||||
find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension})
|
|
||||||
find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension})
|
|
||||||
find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension})
|
|
||||||
find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_LINKER ${cross_prefix}ld${extension})
|
|
||||||
set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags")
|
|
||||||
set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags")
|
|
||||||
set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags")
|
|
||||||
SET(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>")
|
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags")
|
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")
|
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags")
|
|
||||||
|
|
||||||
set(CMAKE_FIND_ROOT_PATH ${DEVKITARM}/aarch64-none-elf ${DEVKITPRO}/portlibs/switch)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")
|
|
||||||
set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE)
|
|
||||||
|
|
||||||
set(SWITCH ON)
|
set(SWITCH ON)
|
||||||
add_definitions(-D__SWITCH__)
|
add_definitions(-D__SWITCH__)
|
||||||
|
|
||||||
|
create_devkit(A64)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${DEVKITA64}/${CMAKE_LIBRARY_ARCHITECTURE} ${DEVKITPRO}/portlibs/switch)
|
||||||
|
|
|
@ -1,49 +1,16 @@
|
||||||
if(DEFINED ENV{DEVKITPRO})
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(DEFINED ENV{DEVKITPPC})
|
|
||||||
set(DEVKITPPC $ENV{DEVKITPPC})
|
|
||||||
else()
|
|
||||||
set(DEVKITPPC ${DEVKITPRO}/devkitPPC)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(extension)
|
|
||||||
if (CMAKE_HOST_WIN32)
|
|
||||||
set(extension .exe)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_PROGRAM_PATH ${DEVKITPPC}/bin)
|
|
||||||
set(cross_prefix powerpc-eabi-)
|
set(cross_prefix powerpc-eabi-)
|
||||||
set(arch_flags "-mrvl -mcpu=750 -meabi -mhard-float -g")
|
set(arch_flags "-mrvl -mcpu=750 -meabi -mhard-float -g")
|
||||||
set(inc_flags "-I${DEVKITPRO}/libogc/include ${arch_flags}")
|
set(inc_flags "-I${DEVKITPRO}/libogc/include ${arch_flags}")
|
||||||
set(link_flags "-L${DEVKITPRO}/libogc/lib/wii ${arch_flags}")
|
set(link_flags "-L${DEVKITPRO}/libogc/lib/wii ${arch_flags}")
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR powerpc CACHE INTERNAL "processor")
|
set(CMAKE_SYSTEM_PROCESSOR powerpc CACHE INTERNAL "processor")
|
||||||
set(CMAKE_LIBRARY_ARCHITECTURE powerpc-none-eabi CACHE INTERNAL "abi")
|
set(CMAKE_LIBRARY_ARCHITECTURE powerpc-none-eabi CACHE INTERNAL "abi")
|
||||||
|
|
||||||
find_program(CMAKE_AR ${cross_prefix}gcc-ar${extension})
|
|
||||||
find_program(CMAKE_RANLIB ${cross_prefix}gcc-ranlib${extension})
|
|
||||||
find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension})
|
|
||||||
find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension})
|
|
||||||
find_program(CMAKE_LINKER ${cross_prefix}ld${extension})
|
|
||||||
set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags")
|
|
||||||
set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags")
|
|
||||||
set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags")
|
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags")
|
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")
|
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS ${link_flags} CACHE INTERNAL "shared link flags")
|
|
||||||
|
|
||||||
set(CMAKE_FIND_ROOT_PATH ${DEVKITPPC}/powerpc-eabi ${DEVKITPRO}/portlibs/ppc)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE INTERNAL "")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")
|
|
||||||
set(PKG_CONFIG_EXECUTABLE "/dev/null" CACHE INTERNAL "" FORCE)
|
|
||||||
|
|
||||||
set(WII ON)
|
set(WII ON)
|
||||||
add_definitions(-DGEKKO)
|
add_definitions(-DGEKKO)
|
||||||
|
|
||||||
|
create_devkit(PPC)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${DEVKITPPC}/powerpc-eabi ${DEVKITPRO}/portlibs/ppc)
|
||||||
|
|
|
@ -25,8 +25,8 @@ size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
|
||||||
size_t RingFIFOSize(const struct RingFIFO* buffer) {
|
size_t RingFIFOSize(const struct RingFIFO* buffer) {
|
||||||
const void* read;
|
const void* read;
|
||||||
const void* write;
|
const void* write;
|
||||||
ATOMIC_LOAD(read, buffer->readPtr);
|
ATOMIC_LOAD_PTR(read, buffer->readPtr);
|
||||||
ATOMIC_LOAD(write, buffer->writePtr);
|
ATOMIC_LOAD_PTR(write, buffer->writePtr);
|
||||||
if (read <= write) {
|
if (read <= write) {
|
||||||
return (uintptr_t) write - (uintptr_t) read;
|
return (uintptr_t) write - (uintptr_t) read;
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,14 +35,14 @@ size_t RingFIFOSize(const struct RingFIFO* buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RingFIFOClear(struct RingFIFO* buffer) {
|
void RingFIFOClear(struct RingFIFO* buffer) {
|
||||||
ATOMIC_STORE(buffer->readPtr, buffer->data);
|
ATOMIC_STORE_PTR(buffer->readPtr, buffer->data);
|
||||||
ATOMIC_STORE(buffer->writePtr, buffer->data);
|
ATOMIC_STORE_PTR(buffer->writePtr, buffer->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
||||||
void* data = buffer->writePtr;
|
void* data = buffer->writePtr;
|
||||||
void* end;
|
void* end;
|
||||||
ATOMIC_LOAD(end, buffer->readPtr);
|
ATOMIC_LOAD_PTR(end, buffer->readPtr);
|
||||||
|
|
||||||
// Wrap around if we can't fit enough in here
|
// Wrap around if we can't fit enough in here
|
||||||
if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
|
if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
|
||||||
|
@ -67,14 +67,14 @@ size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length)
|
||||||
if (value) {
|
if (value) {
|
||||||
memcpy(data, value, length);
|
memcpy(data, value, length);
|
||||||
}
|
}
|
||||||
ATOMIC_STORE(buffer->writePtr, (void*) ((intptr_t) data + length));
|
ATOMIC_STORE_PTR(buffer->writePtr, (void*) ((intptr_t) data + length));
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||||
void* data = buffer->readPtr;
|
void* data = buffer->readPtr;
|
||||||
void* end;
|
void* end;
|
||||||
ATOMIC_LOAD(end, buffer->writePtr);
|
ATOMIC_LOAD_PTR(end, buffer->writePtr);
|
||||||
|
|
||||||
// Wrap around if we can't fit enough in here
|
// Wrap around if we can't fit enough in here
|
||||||
if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
|
if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
|
||||||
|
@ -99,6 +99,6 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||||
if (output) {
|
if (output) {
|
||||||
memcpy(output, data, length);
|
memcpy(output, data, length);
|
||||||
}
|
}
|
||||||
ATOMIC_STORE(buffer->readPtr, (void*) ((uintptr_t) data + length));
|
ATOMIC_STORE_PTR(buffer->readPtr, (void*) ((uintptr_t) data + length));
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#define M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(NAME, ...) M_TEST_SUITE_DEFINE_EX(NAME, _testSuite_setup_ ## NAME, _testSuite_teardown_ ## NAME, __VA_ARGS__)
|
#define M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(NAME, ...) M_TEST_SUITE_DEFINE_EX(NAME, _testSuite_setup_ ## NAME, _testSuite_teardown_ ## NAME, __VA_ARGS__)
|
||||||
#define M_TEST_SUITE_DEFINE_EX(NAME, SETUP, TEARDOWN, ...) \
|
#define M_TEST_SUITE_DEFINE_EX(NAME, SETUP, TEARDOWN, ...) \
|
||||||
int main(void) { \
|
int main(void) { \
|
||||||
const static struct CMUnitTest tests[] = { \
|
static const struct CMUnitTest tests[] = { \
|
||||||
__VA_ARGS__ \
|
__VA_ARGS__ \
|
||||||
}; \
|
}; \
|
||||||
return cmocka_run_group_tests_name(# NAME, tests, SETUP, TEARDOWN); \
|
return cmocka_run_group_tests_name(# NAME, tests, SETUP, TEARDOWN); \
|
||||||
|
|