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)
|
||||
- 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 Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743)
|
||||
Other fixes:
|
||||
- Qt: More app metadata fixes
|
||||
- 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
|
||||
- FFmpeg: Improve initialization reliability and cleanup
|
||||
- Wii: Fix aspect ratio (fixes mgba.io/i/500)
|
||||
- Qt: Fix some Qt display driver race conditions
|
||||
- FFmpeg: Fix audio conversion producing gaps
|
||||
Misc:
|
||||
- GBA Savedata: EEPROM performance fixes
|
||||
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||
|
@ -74,6 +77,8 @@ Misc:
|
|||
- Debugger: Make tracing compatible with breakpoints/watchpoints
|
||||
- Debugger: Print breakpoint/watchpoint number when inserting
|
||||
- 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)
|
||||
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_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
||||
#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_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_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
|
||||
// TODO
|
||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||
#define ATOMIC_ADD(DST, OP) DST += OP
|
||||
#define ATOMIC_SUB(DST, OP) DST -= OP
|
||||
#define ATOMIC_OR(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_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||
#endif
|
||||
|
||||
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
|
||||
|
|
|
@ -23,10 +23,14 @@ struct mLockstep {
|
|||
enum mLockstepPhase transferActive;
|
||||
int32_t transferCycles;
|
||||
|
||||
void (*lock)(struct mLockstep*);
|
||||
void (*unlock)(struct mLockstep*);
|
||||
|
||||
bool (*signal)(struct mLockstep*, unsigned mask);
|
||||
bool (*wait)(struct mLockstep*, unsigned mask);
|
||||
void (*addCycles)(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* context;
|
||||
#ifndef NDEBUG
|
||||
|
@ -35,6 +39,19 @@ 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
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ struct ARMMemory {
|
|||
uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
||||
int* cycleCounter);
|
||||
|
||||
uint32_t* activeRegion;
|
||||
const uint32_t* activeRegion;
|
||||
uint32_t activeMask;
|
||||
uint32_t activeSeqCycles32;
|
||||
uint32_t activeSeqCycles16;
|
||||
|
|
|
@ -56,7 +56,7 @@ struct LR35902Memory {
|
|||
|
||||
int (*currentSegment)(struct LR35902Core*, uint16_t address);
|
||||
|
||||
uint8_t* activeRegion;
|
||||
const uint8_t* activeRegion;
|
||||
uint16_t activeMask;
|
||||
uint16_t activeRegionEnd;
|
||||
void (*setActiveRegion)(struct LR35902Core*, uint16_t address);
|
||||
|
|
|
@ -230,7 +230,7 @@ bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) {
|
|||
char directive[64];
|
||||
ssize_t len = snprintf(directive, sizeof(directive) - 1, "!%s\n", *StringListGetPointer(&directives, d));
|
||||
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) {
|
||||
char name[PATH_MAX];
|
||||
char name[PATH_MAX + 14]; // Quash warning
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
int result = sqlite3_exec(library->db,
|
||||
sqlite3_exec(library->db,
|
||||
" BEGIN TRANSACTION;"
|
||||
"\n DELETE FROM roots;"
|
||||
"\n DELETE FROM roms;"
|
||||
|
|
|
@ -11,6 +11,12 @@ void mLockstepInit(struct mLockstep* lockstep) {
|
|||
#ifndef NDEBUG
|
||||
lockstep->transferId = 0;
|
||||
#endif
|
||||
lockstep->lock = NULL;
|
||||
lockstep->unlock = NULL;
|
||||
}
|
||||
|
||||
void mLockstepDeinit(struct mLockstep* lockstep) {
|
||||
UNUSED(lockstep);
|
||||
}
|
||||
|
||||
// TODO: Migrate nodes
|
||||
|
|
|
@ -14,6 +14,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextE
|
|||
}
|
||||
|
||||
void mTimingDeinit(struct mTiming* timing) {
|
||||
UNUSED(timing);
|
||||
}
|
||||
|
||||
void mTimingClear(struct mTiming* timing) {
|
||||
|
|
|
@ -263,6 +263,8 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
|
|||
free((void*) dbType);
|
||||
free((void*) dbVersion);
|
||||
|
||||
sqlite3_finalize(gamedbTable);
|
||||
sqlite3_finalize(gamedbDrop);
|
||||
sqlite3_finalize(gameTable);
|
||||
sqlite3_finalize(romTable);
|
||||
|
||||
|
@ -275,6 +277,7 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
|
|||
}
|
||||
|
||||
void NoIntroDBDestroy(struct NoIntroDB* db) {
|
||||
sqlite3_finalize(db->crc32);
|
||||
sqlite3_close(db->db);
|
||||
free(db);
|
||||
}
|
||||
|
|
|
@ -508,7 +508,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
|
|||
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy == y && wx <= endX) {
|
||||
softwareRenderer->hasWindow = true;
|
||||
}
|
||||
if (softwareRenderer->hasWindow && wx <= endX) {
|
||||
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->hasWindow && wx <= endX) {
|
||||
if (wx > 0 && !softwareRenderer->d.disableBG) {
|
||||
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);
|
||||
|
||||
void GBSIOLockstepInit(struct GBSIOLockstep* lockstep) {
|
||||
mLockstepInit(&lockstep->d);
|
||||
lockstep->players[0] = NULL;
|
||||
lockstep->players[1] = NULL;
|
||||
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, &node->event);
|
||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||
} else {
|
||||
mLOG(GB_SIO, FATAL, "GBSIOLockstepNodeWriteSC() failed to write to masterClaimed\n");
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
|
@ -94,6 +94,7 @@ void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
|
|||
}
|
||||
|
||||
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
struct GBASIOBattlechipGate* gate = user;
|
||||
|
||||
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];
|
||||
|
||||
#define LOAD_VRAM \
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
||||
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
} else { \
|
||||
LOAD_32(value, address & 0x00017FFC, gba->video.vram); \
|
||||
if ((address & 0x0001FFFF) >= SIZE_VRAM) { \
|
||||
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load32: 0x%08X", address); \
|
||||
value = 0; \
|
||||
break; \
|
||||
} \
|
||||
address &= 0x00017FFC; \
|
||||
} \
|
||||
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
wait += waitstatesRegion[REGION_VRAM];
|
||||
|
||||
#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);
|
||||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
} else {
|
||||
LOAD_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load16: 0x%08X", address);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
address &= 0x00017FFE;
|
||||
}
|
||||
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
break;
|
||||
case REGION_OAM:
|
||||
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)];
|
||||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
||||
} else {
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF];
|
||||
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load8: 0x%08X", address);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
address &= 0x00017FFF;
|
||||
}
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
||||
break;
|
||||
case REGION_OAM:
|
||||
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];
|
||||
|
||||
#define STORE_VRAM \
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
||||
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); \
|
||||
if (oldValue != value) { \
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||
} \
|
||||
} else { \
|
||||
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)); \
|
||||
if ((address & 0x0001FFFF) >= SIZE_VRAM) { \
|
||||
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store32: 0x%08X", address); \
|
||||
break; \
|
||||
} \
|
||||
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];
|
||||
|
||||
|
@ -840,18 +850,17 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
}
|
||||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
|
||||
if (value != oldValue) {
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
if ((address & 0x0001FFFF) >= SIZE_VRAM) {
|
||||
if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store16: 0x%08X", address);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
case REGION_OAM:
|
||||
|
@ -939,7 +948,6 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -67,12 +67,8 @@
|
|||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||
current = renderer->spriteLayer[outX]; \
|
||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
||||
if (tileData) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
} else if (current != FLAG_UNWRITTEN) { \
|
||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
||||
} \
|
||||
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_16_NORMAL_OBJWIN(localX) \
|
||||
|
@ -84,13 +80,9 @@
|
|||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||
tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
|
||||
current = renderer->spriteLayer[outX]; \
|
||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
||||
if (tileData) { \
|
||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||
renderer->spriteLayer[outX] = color | flags; \
|
||||
} else if (current != FLAG_UNWRITTEN) { \
|
||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
||||
} \
|
||||
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||
renderer->spriteLayer[outX] = color | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
|
||||
|
@ -117,12 +109,8 @@
|
|||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||
current = renderer->spriteLayer[outX]; \
|
||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
||||
if (tileData) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
} else if (current != FLAG_UNWRITTEN) { \
|
||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
||||
} \
|
||||
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||
renderer->spriteLayer[outX] = palette[tileData] | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_256_NORMAL_OBJWIN(localX) \
|
||||
|
@ -134,13 +122,9 @@
|
|||
LOAD_16(tileData, spriteBase & VRAM_BLOCK_MASK, vramBase); \
|
||||
tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
|
||||
current = renderer->spriteLayer[outX]; \
|
||||
if ((current & FLAG_ORDER_MASK) > flags) { \
|
||||
if (tileData) { \
|
||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||
renderer->spriteLayer[outX] = color | flags; \
|
||||
} else if (current != FLAG_UNWRITTEN) { \
|
||||
renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
|
||||
} \
|
||||
if ((current & FLAG_UNWRITTEN) == FLAG_UNWRITTEN && tileData) { \
|
||||
unsigned color = (renderer->row[outX] & FLAG_OBJWIN) ? objwinPalette[tileData] : palette[tileData]; \
|
||||
renderer->spriteLayer[outX] = color | flags; \
|
||||
}
|
||||
|
||||
#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include <mgba/internal/gba/gba.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 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 GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
|
||||
static void _finishTransfer(struct GBASIOLockstepNode* node);
|
||||
|
||||
void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
||||
mLockstepInit(&lockstep->d);
|
||||
lockstep->players[0] = 0;
|
||||
lockstep->players[1] = 0;
|
||||
lockstep->players[2] = 0;
|
||||
|
@ -88,12 +89,16 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
|||
node->nextEvent = 0;
|
||||
node->eventDiff = 0;
|
||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||
|
||||
mLockstepLock(&node->p->d);
|
||||
|
||||
node->mode = driver->p->mode;
|
||||
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister;
|
||||
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;
|
||||
if (node->id) {
|
||||
node->d.p->rcnt |= 4;
|
||||
|
@ -110,35 +115,83 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
|||
node->phase = node->p->d.transferActive;
|
||||
node->transferId = node->p->d.transferId;
|
||||
#endif
|
||||
|
||||
mLockstepUnlock(&node->p->d);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
|
||||
mLockstepLock(&node->p->d);
|
||||
|
||||
node->mode = driver->p->mode;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
--node->p->attachedMulti;
|
||||
ATOMIC_SUB(node->p->attachedMulti, 1);
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
|
||||
mLockstepLock(&node->p->d);
|
||||
|
||||
if (address == REG_SIOCNT) {
|
||||
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) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
||||
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.transferActive, TRANSFER_STARTING);
|
||||
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);
|
||||
mTimingSchedule(&driver->p->p->timing, &node->event, 0);
|
||||
|
||||
if (scheduled) {
|
||||
node->eventDiff -= oldWhen - node->event.when;
|
||||
}
|
||||
} else {
|
||||
value &= ~0x0080;
|
||||
}
|
||||
|
@ -148,6 +201,9 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
|
|||
} else if (address == REG_SIOMLT_SEND) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value);
|
||||
}
|
||||
|
||||
mLockstepUnlock(&node->p->d);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -155,6 +211,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
if (node->transferFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBASIO* sio = node->d.p;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
|
@ -209,27 +266,38 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
||||
bool needsToWait = false;
|
||||
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:
|
||||
// If the master hasn't initiated a transfer, it can keep going.
|
||||
node->nextEvent += LOCKSTEP_INCREMENT;
|
||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached;
|
||||
node->d.p->multiplayerControl.ready = attachedMulti == attached;
|
||||
break;
|
||||
case TRANSFER_STARTING:
|
||||
// Start the transfer, but wait for the other GBAs to catch up
|
||||
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[2] = 0xFFFF;
|
||||
node->p->multiRecv[3] = 0xFFFF;
|
||||
needsToWait = true;
|
||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTED);
|
||||
node->nextEvent += 512;
|
||||
node->nextEvent += LOCKSTEP_TRANSFER;
|
||||
break;
|
||||
case TRANSFER_STARTED:
|
||||
// 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 += 512;
|
||||
node->nextEvent += LOCKSTEP_TRANSFER;
|
||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHING);
|
||||
break;
|
||||
case TRANSFER_FINISHING:
|
||||
|
@ -269,6 +337,7 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
|
|||
#ifndef NDEBUG
|
||||
node->phase = node->p->d.transferActive;
|
||||
#endif
|
||||
|
||||
if (needsToWait) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -276,9 +345,16 @@ static int32_t _masterUpdate(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;
|
||||
switch (node->p->d.transferActive) {
|
||||
switch (transferActive) {
|
||||
case TRANSFER_IDLE:
|
||||
if (!node->d.p->multiplayerControl.ready) {
|
||||
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:
|
||||
break;
|
||||
case TRANSFER_STARTED:
|
||||
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||
break;
|
||||
}
|
||||
node->transferFinished = false;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
|
@ -315,6 +394,9 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
|||
signal = true;
|
||||
break;
|
||||
case TRANSFER_FINISHED:
|
||||
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||
break;
|
||||
}
|
||||
_finishTransfer(node);
|
||||
signal = true;
|
||||
break;
|
||||
|
@ -325,16 +407,20 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
|
|||
if (signal) {
|
||||
node->p->d.signal(&node->p->d, 1 << node->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct GBASIOLockstepNode* node = user;
|
||||
mLockstepLock(&node->p->d);
|
||||
if (node->p->d.attached < 2) {
|
||||
mLockstepUnlock(&node->p->d);
|
||||
return;
|
||||
}
|
||||
int32_t cycles = 0;
|
||||
node->nextEvent -= cyclesLate;
|
||||
node->eventDiff += cyclesLate;
|
||||
if (node->nextEvent <= 0) {
|
||||
if (!node->id) {
|
||||
cycles = _masterUpdate(node);
|
||||
|
@ -353,12 +439,18 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
|
|||
mTimingSchedule(timing, &node->event, cycles);
|
||||
} else {
|
||||
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) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
|
||||
mLockstepLock(&node->p->d);
|
||||
|
||||
if (address == REG_SIOCNT) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
||||
value &= 0xFF8B;
|
||||
|
@ -368,7 +460,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
if (value & 0x0080 && !node->id) {
|
||||
// Internal shift clock
|
||||
if (value & 1) {
|
||||
node->p->d.transferActive = TRANSFER_STARTING;
|
||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
||||
}
|
||||
// Frequency
|
||||
if (value & 2) {
|
||||
|
@ -382,5 +474,8 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
} else if (address == REG_SIODATA32_HI) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value);
|
||||
}
|
||||
|
||||
mLockstepUnlock(&node->p->d);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
if(DEFINED ENV{DEVKITPRO})
|
||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{DEVKITARM})
|
||||
set(DEVKITARM $ENV{DEVKITARM})
|
||||
else()
|
||||
set(DEVKITARM ${DEVKITPRO}/devkitARM)
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||
|
||||
if(DEFINED ENV{CTRULIB})
|
||||
set(CTRULIB $ENV{CTRULIB})
|
||||
|
@ -16,40 +6,17 @@ else()
|
|||
set(CTRULIB ${DEVKITPRO}/libctru)
|
||||
endif()
|
||||
|
||||
set(extension)
|
||||
if (CMAKE_HOST_WIN32)
|
||||
set(extension .exe)
|
||||
endif()
|
||||
|
||||
set(CMAKE_PROGRAM_PATH ${DEVKITARM}/bin)
|
||||
set(cross_prefix arm-none-eabi-)
|
||||
set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections")
|
||||
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(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor")
|
||||
set(CMAKE_LIBRARY_ARCHITECTURE arm-none-eabi CACHE INTERNAL "abi")
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shortcuts) {
|
||||
void ActionMapper::rebuildMenu(QMenuBar* menubar, QWidget* context, const ShortcutController& shortcuts) {
|
||||
menubar->clear();
|
||||
for (QAction* action : context->actions()) {
|
||||
context->removeAction(action);
|
||||
}
|
||||
for (const QString& m : m_menus[{}]) {
|
||||
if (m_hiddenActions.contains(m)) {
|
||||
continue;
|
||||
|
@ -39,11 +42,11 @@ void ActionMapper::rebuildMenu(QMenuBar* menubar, const ShortcutController& shor
|
|||
QString menu = m.mid(1);
|
||||
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]) {
|
||||
if (actionName.isNull()) {
|
||||
qmenu->addSeparator();
|
||||
|
@ -55,12 +58,13 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const Shortcut
|
|||
if (actionName[0] == '.') {
|
||||
QString name = actionName.mid(1);
|
||||
QMenu* newMenu = qmenu->addMenu(m_menuNames[name]);
|
||||
rebuildMenu(name, newMenu, shortcuts);
|
||||
rebuildMenu(name, newMenu, context, shortcuts);
|
||||
continue;
|
||||
}
|
||||
Action* action = &m_actions[actionName];
|
||||
QAction* qaction = qmenu->addAction(action->visibleName());
|
||||
qaction->setEnabled(action->isEnabled());
|
||||
qaction->setShortcutContext(Qt::WidgetShortcut);
|
||||
if (action->isExclusive() || action->booleanAction()) {
|
||||
qaction->setCheckable(true);
|
||||
}
|
||||
|
@ -88,6 +92,7 @@ void ActionMapper::rebuildMenu(const QString& menu, QMenu* qmenu, const 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 addHiddenMenu(const QString& visibleName, const QString& name, const QString& parent = {});
|
||||
void clearMenu(const QString& name);
|
||||
void rebuildMenu(QMenuBar*, const ShortcutController&);
|
||||
void rebuildMenu(QMenuBar*, QWidget* context, const ShortcutController&);
|
||||
|
||||
void addSeparator(const QString& menu);
|
||||
|
||||
|
@ -59,7 +59,7 @@ signals:
|
|||
void menuCleared(const QString& name);
|
||||
|
||||
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);
|
||||
|
||||
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_gl->setMouseTracking(true);
|
||||
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
||||
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
||||
}
|
||||
|
||||
DisplayGL::~DisplayGL() {
|
||||
|
@ -223,6 +224,9 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
|
|||
#endif
|
||||
m_backend->swap = [](VideoBackend* v) {
|
||||
PainterGL* painter = static_cast<PainterGL*>(v->user);
|
||||
if (!painter->m_gl->isVisible()) {
|
||||
return;
|
||||
}
|
||||
painter->m_gl->swapBuffers();
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
EmptyGLWidget(const QGLFormat& format, QWidget* parent) : QGLWidget(format, parent) { setAutoBufferSwap(false); }
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) override {}
|
||||
void paintEvent(QPaintEvent* event) override { event->ignore(); }
|
||||
void resizeEvent(QResizeEvent*) override {}
|
||||
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
|
|
@ -79,7 +79,9 @@ void MessagePainter::redraw() {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
|
||||
: controller(coreController)
|
||||
, gbNode(node)
|
||||
{
|
||||
}
|
||||
|
||||
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
|
||||
: controller(coreController)
|
||||
, gbaNode(node)
|
||||
{
|
||||
}
|
||||
|
||||
MultiplayerController::MultiplayerController() {
|
||||
mLockstepInit(&m_lockstep);
|
||||
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) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
Player* player = &controller->m_players[0];
|
||||
bool woke = false;
|
||||
controller->m_lock.lock();
|
||||
player->waitMask &= ~mask;
|
||||
if (!player->waitMask && player->awake < 1) {
|
||||
mCoreThreadStopWaiting(player->controller->thread());
|
||||
player->awake = 1;
|
||||
woke = true;
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
return woke;
|
||||
};
|
||||
m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
controller->m_lock.lock();
|
||||
Player* player = &controller->m_players[0];
|
||||
bool slept = false;
|
||||
player->waitMask |= mask;
|
||||
|
@ -44,7 +61,6 @@ MultiplayerController::MultiplayerController() {
|
|||
player->awake = 0;
|
||||
slept = true;
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
return slept;
|
||||
};
|
||||
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||
|
@ -52,7 +68,6 @@ MultiplayerController::MultiplayerController() {
|
|||
abort();
|
||||
}
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
controller->m_lock.lock();
|
||||
if (!id) {
|
||||
for (int i = 1; i < controller->m_players.count(); ++i) {
|
||||
Player* player = &controller->m_players[i];
|
||||
|
@ -85,11 +100,9 @@ MultiplayerController::MultiplayerController() {
|
|||
controller->m_players[id].controller->setSync(true);
|
||||
controller->m_players[id].cyclesPosted += cycles;
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
};
|
||||
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
controller->m_lock.lock();
|
||||
Player* player = &controller->m_players[id];
|
||||
player->cyclesPosted -= cycles;
|
||||
if (player->cyclesPosted <= 0) {
|
||||
|
@ -97,15 +110,23 @@ MultiplayerController::MultiplayerController() {
|
|||
player->awake = 0;
|
||||
}
|
||||
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;
|
||||
};
|
||||
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
controller->m_lock.lock();
|
||||
Player* player = &controller->m_players[id];
|
||||
if (id) {
|
||||
Player* player = &controller->m_players[id];
|
||||
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);
|
||||
if (!player->waitMask && player->awake < 1) {
|
||||
mCoreThreadStopWaiting(player->controller->thread());
|
||||
|
@ -149,10 +170,13 @@ MultiplayerController::MultiplayerController() {
|
|||
}
|
||||
}
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
};
|
||||
}
|
||||
|
||||
MultiplayerController::~MultiplayerController() {
|
||||
mLockstepDeinit(&m_lockstep);
|
||||
}
|
||||
|
||||
bool MultiplayerController::attachGame(CoreController* controller) {
|
||||
if (m_lockstep.attached == MAX_GBAS) {
|
||||
return false;
|
||||
|
@ -188,14 +212,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
|
|||
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
||||
GBASIOLockstepNodeCreate(node);
|
||||
GBASIOLockstepAttachNode(&m_gbaLockstep, node);
|
||||
m_players.append({
|
||||
controller,
|
||||
nullptr,
|
||||
node,
|
||||
1,
|
||||
0,
|
||||
0
|
||||
});
|
||||
m_players.append({controller, node});
|
||||
|
||||
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
|
||||
|
||||
|
@ -210,14 +227,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
|
|||
GBSIOLockstepNode* node = new GBSIOLockstepNode;
|
||||
GBSIOLockstepNodeCreate(node);
|
||||
GBSIOLockstepAttachNode(&m_gbLockstep, node);
|
||||
m_players.append({
|
||||
controller,
|
||||
node,
|
||||
nullptr,
|
||||
1,
|
||||
0,
|
||||
0
|
||||
});
|
||||
m_players.append({controller, node});
|
||||
|
||||
GBSIOSetDriver(&gb->sio, &node->d);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <mgba/internal/gb/sio/lockstep.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct GBSIOLockstepNode;
|
||||
struct GBASIOLockstepNode;
|
||||
|
||||
|
@ -29,6 +31,7 @@ Q_OBJECT
|
|||
|
||||
public:
|
||||
MultiplayerController();
|
||||
~MultiplayerController();
|
||||
|
||||
bool attachGame(CoreController*);
|
||||
void detachGame(CoreController*);
|
||||
|
@ -42,12 +45,15 @@ signals:
|
|||
|
||||
private:
|
||||
struct Player {
|
||||
Player(CoreController* controller, GBSIOLockstepNode* node);
|
||||
Player(CoreController* controller, GBASIOLockstepNode* node);
|
||||
|
||||
CoreController* controller;
|
||||
GBSIOLockstepNode* gbNode;
|
||||
GBASIOLockstepNode* gbaNode;
|
||||
int awake;
|
||||
int32_t cyclesPosted;
|
||||
unsigned waitMask;
|
||||
GBSIOLockstepNode* gbNode = nullptr;
|
||||
GBASIOLockstepNode* gbaNode = nullptr;
|
||||
int awake = 1;
|
||||
int32_t cyclesPosted = 0;
|
||||
unsigned waitMask = 0;
|
||||
};
|
||||
union {
|
||||
mLockstep m_lockstep;
|
||||
|
|
|
@ -279,7 +279,11 @@ void VideoView::setAudioCodec(const QString& codec, bool manual) {
|
|||
void VideoView::setVideoCodec(const QString& codec, bool manual) {
|
||||
free(m_videoCodecCstr);
|
||||
m_videoCodec = sanitizeCodec(codec, s_vcodecMap);
|
||||
m_videoCodecCstr = strdup(m_videoCodec.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)) {
|
||||
free(m_videoCodecCstr);
|
||||
m_videoCodecCstr = nullptr;
|
||||
|
|
|
@ -608,9 +608,7 @@ void Window::resizeEvent(QResizeEvent* event) {
|
|||
}
|
||||
m_savedScale = factor;
|
||||
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()->blockSignals(enableSignals);
|
||||
}
|
||||
|
||||
m_config->setOption("fullscreen", isFullScreen());
|
||||
|
@ -636,6 +634,7 @@ void Window::showEvent(QShowEvent* event) {
|
|||
m_fullscreenOnStart = false;
|
||||
}
|
||||
reloadDisplayDriver();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void Window::closeEvent(QCloseEvent* event) {
|
||||
|
@ -835,7 +834,7 @@ void Window::gameStarted() {
|
|||
action->setActive(true);
|
||||
}
|
||||
}
|
||||
m_actions.rebuildMenu(menuBar(), *m_shortcutController);
|
||||
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||
|
||||
|
||||
#ifdef USE_DISCORD_RPC
|
||||
|
@ -1382,9 +1381,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_savedScale = i;
|
||||
m_config->setOption("scaleMultiplier", i); // TODO: Port to other
|
||||
resizeFrame(size);
|
||||
bool enableSignals = setSize->blockSignals(true);
|
||||
setSize->setActive(true);
|
||||
setSize->blockSignals(enableSignals);
|
||||
}, "frame");
|
||||
setSize->setExclusive(true);
|
||||
if (m_savedScale == i) {
|
||||
|
@ -1481,18 +1478,13 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
#endif
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
addGameAction(tr("Record output..."), "recordOutput", this, &Window::openVideoWindow, "av");
|
||||
addGameAction(tr("Record A/V..."), "recordOutput", this, &Window::openVideoWindow, "av");
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAGICK
|
||||
addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av");
|
||||
#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.addMenu(tr("Video layers"), "videoLayers", "av");
|
||||
m_actions.addMenu(tr("Audio channels"), "audioChannels", "av");
|
||||
|
@ -1552,6 +1544,12 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_platformActions.insert(PLATFORM_GBA, ioViewer);
|
||||
#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");
|
||||
skipBios->connect([this](const QVariant& value) {
|
||||
reloadConfig();
|
||||
|
@ -1642,7 +1640,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
}
|
||||
|
||||
m_shortcutController->rebuildItems();
|
||||
m_actions.rebuildMenu(menubar, *m_shortcutController);
|
||||
m_actions.rebuildMenu(menuBar(), this, *m_shortcutController);
|
||||
}
|
||||
|
||||
void Window::attachWidget(QWidget* widget) {
|
||||
|
@ -1679,7 +1677,7 @@ void Window::updateMRU() {
|
|||
}
|
||||
m_config->setMRU(m_mruFiles);
|
||||
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) {
|
||||
|
|
|
@ -60,7 +60,7 @@ set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/main.c)
|
|||
|
||||
if(BUILD_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)
|
||||
set(OPENGLES2_LIBRARY "-lEGL -lGLESv2 -lbcm_host")
|
||||
set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE)
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include "main.h"
|
||||
|
||||
#include "gl-common.h"
|
||||
#ifdef BUILD_RASPI
|
||||
#include "rpi-common.h"
|
||||
#endif
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/thread.h>
|
||||
|
@ -26,74 +29,7 @@ void mSDLGLES2Create(struct mSDLRenderer* renderer) {
|
|||
|
||||
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||
#ifdef BUILD_RASPI
|
||||
bcm_host_init();
|
||||
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;
|
||||
}
|
||||
mRPIGLCommonInit(renderer);
|
||||
#else
|
||||
mSDLGLCommonInit(renderer);
|
||||
#endif
|
||||
|
@ -112,7 +48,11 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
|||
renderer->gl2.d.lockAspectRatio = renderer->lockAspectRatio;
|
||||
renderer->gl2.d.lockIntegerScaling = renderer->lockIntegerScaling;
|
||||
renderer->gl2.d.filter = renderer->filter;
|
||||
#ifdef BUILD_RASPI
|
||||
renderer->gl2.d.swap = mRPIGLCommonSwap;
|
||||
#else
|
||||
renderer->gl2.d.swap = mSDLGLCommonSwap;
|
||||
#endif
|
||||
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
||||
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);
|
||||
v->drawFrame(v);
|
||||
#ifdef BUILD_RASPI
|
||||
eglSwapBuffers(renderer->display, renderer->surface);
|
||||
#else
|
||||
v->swap(v);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,10 +96,10 @@ void mSDLGLES2Deinit(struct mSDLRenderer* renderer) {
|
|||
renderer->gl2.d.deinit(&renderer->gl2.d);
|
||||
}
|
||||
#ifdef BUILD_RASPI
|
||||
eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(renderer->display, renderer->surface);
|
||||
eglDestroyContext(renderer->display, renderer->context);
|
||||
eglTerminate(renderer->display);
|
||||
eglMakeCurrent(renderer->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(renderer->eglDisplay, renderer->eglSurface);
|
||||
eglDestroyContext(renderer->eglDisplay, renderer->eglContext);
|
||||
eglTerminate(renderer->eglDisplay);
|
||||
bcm_host_deinit();
|
||||
#elif SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_GL_DeleteContext(renderer->glCtx);
|
||||
|
|
|
@ -81,10 +81,10 @@ struct mSDLRenderer {
|
|||
#endif
|
||||
|
||||
#ifdef BUILD_RASPI
|
||||
EGLDisplay display;
|
||||
EGLSurface surface;
|
||||
EGLContext context;
|
||||
EGL_DISPMANX_WINDOW_T window;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLSurface eglSurface;
|
||||
EGLContext eglContext;
|
||||
EGL_DISPMANX_WINDOW_T eglWindow;
|
||||
#endif
|
||||
|
||||
#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})
|
||||
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()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||
|
||||
if(DEFINED ENV{LIBNX})
|
||||
set(LIBNX $ENV{LIBNX})
|
||||
|
@ -16,41 +6,17 @@ else()
|
|||
set(LIBNX ${DEVKITPRO}/libnx)
|
||||
endif()
|
||||
|
||||
set(extension)
|
||||
if (CMAKE_HOST_WIN32)
|
||||
set(extension .exe)
|
||||
endif()
|
||||
|
||||
set(CMAKE_PROGRAM_PATH ${DEVKITA64}/bin)
|
||||
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(inc_flags "-I${LIBNX}/include ${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_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)
|
||||
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})
|
||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find DEVKITPRO in environment")
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/devkitPro.cmake)
|
||||
|
||||
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(arch_flags "-mrvl -mcpu=750 -meabi -mhard-float -g")
|
||||
set(inc_flags "-I${DEVKITPRO}/libogc/include ${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_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)
|
||||
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) {
|
||||
const void* read;
|
||||
const void* write;
|
||||
ATOMIC_LOAD(read, buffer->readPtr);
|
||||
ATOMIC_LOAD(write, buffer->writePtr);
|
||||
ATOMIC_LOAD_PTR(read, buffer->readPtr);
|
||||
ATOMIC_LOAD_PTR(write, buffer->writePtr);
|
||||
if (read <= write) {
|
||||
return (uintptr_t) write - (uintptr_t) read;
|
||||
} else {
|
||||
|
@ -35,14 +35,14 @@ size_t RingFIFOSize(const struct RingFIFO* buffer) {
|
|||
}
|
||||
|
||||
void RingFIFOClear(struct RingFIFO* buffer) {
|
||||
ATOMIC_STORE(buffer->readPtr, buffer->data);
|
||||
ATOMIC_STORE(buffer->writePtr, buffer->data);
|
||||
ATOMIC_STORE_PTR(buffer->readPtr, buffer->data);
|
||||
ATOMIC_STORE_PTR(buffer->writePtr, buffer->data);
|
||||
}
|
||||
|
||||
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length) {
|
||||
void* data = buffer->writePtr;
|
||||
void* end;
|
||||
ATOMIC_LOAD(end, buffer->readPtr);
|
||||
ATOMIC_LOAD_PTR(end, buffer->readPtr);
|
||||
|
||||
// Wrap around if we can't fit enough in here
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
|
||||
void* data = buffer->readPtr;
|
||||
void* end;
|
||||
ATOMIC_LOAD(end, buffer->writePtr);
|
||||
ATOMIC_LOAD_PTR(end, buffer->writePtr);
|
||||
|
||||
// Wrap around if we can't fit enough in here
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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_EX(NAME, SETUP, TEARDOWN, ...) \
|
||||
int main(void) { \
|
||||
const static struct CMUnitTest tests[] = { \
|
||||
static const struct CMUnitTest tests[] = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
return cmocka_run_group_tests_name(# NAME, tests, SETUP, TEARDOWN); \
|
||||
|
|