Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Sérgio Benjamim 2018-10-18 21:19:51 -03:00
commit 0a79981bfa
52 changed files with 493 additions and 300 deletions

17
CHANGES
View File

@ -1,3 +1,10 @@
0.8.0: (Future)
Bugfixes:
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
Misc:
- GBA Savedata: EEPROM performance fixes
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
0.7.0: (Future)
Features:
- ELF support
@ -106,6 +113,8 @@ Features:
- Switch: Rotation support
- Qt: State file load/save menu options
- Windows installer
- Tile viewer now has adjustable width
- Python: Experimental audio API
Bugfixes:
- PSP2: Fix audio crackling after fast forward
- PSP2: Fix audio crackling when buffer is full
@ -119,6 +128,9 @@ Bugfixes:
- GB, GBA Savedata: Fix leaks when loading masked save (fixes mgba.io/i/1197)
- Qt: Fix focus issues with load/save state overlay
- GB Video: Fix SGB border hole size
- PSP2: Fix tearing issues (fixes mgba.io/i/1211)
- Qt: Fix mapping analog triggers (fixes mgba.io/i/495)
- Qt: Grab focus when game starts (fixes mgba.io/i/804)
Misc:
- mGUI: Add SGB border configuration option
- mGUI: Add support for different settings types
@ -128,6 +140,11 @@ Misc:
- Qt: Ensure camera image is valid
- GB: Improved SGB2 support
- Libretro: Reduce rumble callbacks
- Debugger: Minor text fixes
- Qt: Debugger console history
- Qt: Detect presence of GL_ARB_framebuffer_object
- Python: Minor API improvements
- Qt: Minor memory view tweaks
0.7 beta 1: (2018-09-24)
- Initial beta for 0.7

View File

@ -44,6 +44,7 @@ struct mVideoLogContext;
struct mCore {
void* cpu;
void* board;
struct mTiming* timing;
struct mDebugger* debugger;
struct mDebuggerSymbols* symbolTable;

View File

@ -55,21 +55,23 @@
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
#define ARM_WRITE_PC \
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
currentCycles += 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
static inline int32_t ARMWritePC(struct ARMCore* cpu) {
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM);
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
cpu->gprs[ARM_PC] += WORD_SIZE_ARM;
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
return 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
}
#define THUMB_WRITE_PC \
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
currentCycles += 2 + cpu->memory.activeNonseqCycles16 + cpu->memory.activeSeqCycles16;
static inline int32_t ThumbWritePC(struct ARMCore* cpu) {
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB);
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
return 2 + cpu->memory.activeNonseqCycles16 + cpu->memory.activeSeqCycles16;
}
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
return mode != MODE_SYSTEM && mode != MODE_USER;

View File

@ -82,6 +82,8 @@ struct GBA {
struct GBATimer timers[4];
int springIRQ;
struct mTimingEvent irqEvent;
uint32_t biosChecksum;
int* keySource;
struct mRotationSource* rotationSource;
@ -141,8 +143,6 @@ void GBADestroy(struct GBA* gba);
void GBAReset(struct ARMCore* cpu);
void GBASkipBIOS(struct GBA* gba);
void GBAWriteIE(struct GBA* gba, uint16_t value);
void GBAWriteIME(struct GBA* gba, uint16_t value);
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
void GBATestIRQ(struct ARMCore* cpu);
void GBAHalt(struct GBA* gba);

View File

@ -23,7 +23,8 @@ enum SavedataType {
SAVEDATA_SRAM = 1,
SAVEDATA_FLASH512 = 2,
SAVEDATA_FLASH1M = 3,
SAVEDATA_EEPROM = 4
SAVEDATA_EEPROM = 4,
SAVEDATA_EEPROM512 = 5
};
enum SavedataCommand {

View File

@ -98,28 +98,28 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x00202 - 0x00203: Old reload value
* | 0x00204 - 0x00207: Last event
* | 0x00208 - 0x0020B: Next event
* | 0x0020C - 0x0020F: Next IRQ
* | 0x0020C - 0x0020F: Reserved
* | 0x00210 - 0x00213: Miscellaneous flags
* 0x00214 - 0x00227: Timer 1
* | 0x00214 - 0x00215: Reload value
* | 0x00216 - 0x00217: Old reload value
* | 0x00218 - 0x0021B: Last event
* | 0x0021C - 0x0021F: Next event
* | 0x00220 - 0x00223: Next IRQ
* | 0x00220 - 0x00223: Reserved
* | 0x00224 - 0x00227: Miscellaneous flags
* 0x00228 - 0x0023B: Timer 2
* | 0x00228 - 0x00229: Reload value
* | 0x0022A - 0x0022B: Old reload value
* | 0x0022C - 0x0022F: Last event
* | 0x00230 - 0x00233: Next event
* | 0x00234 - 0x00237: Next IRQ
* | 0x00234 - 0x00237: Reserved
* | 0x00238 - 0x0023B: Miscellaneous flags
* 0x0023C - 0x00250: Timer 3
* | 0x0023C - 0x0023D: Reload value
* | 0x0023E - 0x0023F: Old reload value
* | 0x00240 - 0x00243: Last event
* | 0x00244 - 0x00247: Next event
* | 0x00248 - 0x0024B: Next IRQ
* | 0x00248 - 0x0024B: Reserved
* | 0x0024C - 0x0024F: Miscellaneous flags
* 0x00250 - 0x0025F: DMA 0
* | 0x00250 - 0x00253: DMA next source
@ -196,7 +196,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x0031C - 0x0031F: Miscellaneous flags
* | bit 0: Is CPU halted?
* | bit 1: POSTFLG
* 0x00320 - 0x003FF: Reserved (leave zero)
* | bit 2: Is IRQ pending?
* 0x00320 - 0x00323: Next IRQ event
* 0x00324 - 0x003FF: Reserved (leave zero)
* 0x00400 - 0x007FF: I/O memory
* 0x00800 - 0x00BFF: Palette
* 0x00C00 - 0x00FFF: OAM
@ -227,6 +229,7 @@ DECL_BIT(GBASerializedSavedataFlags, DustSettling, 5);
DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
DECL_BIT(GBASerializedMiscFlags, Halted, 0);
DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1);
DECL_BIT(GBASerializedMiscFlags, IrqPending, 2);
struct GBASerializedState {
uint32_t versionMagic;
@ -267,10 +270,10 @@ struct GBASerializedState {
struct {
uint16_t reload;
uint16_t reserved;
uint16_t reserved0;
uint32_t lastEvent;
uint32_t nextEvent;
uint32_t nextIrq;
uint32_t reserved1;
GBATimerFlags flags;
} timers[4];
@ -320,8 +323,9 @@ struct GBASerializedState {
uint32_t lastPrefetchedPc;
GBASerializedMiscFlags miscFlags;
uint32_t nextIrq;
uint32_t reserved[56];
uint32_t reserved[55];
uint16_t io[SIZE_IO >> 1];
uint16_t pram[SIZE_PALETTE_RAM >> 1];

View File

@ -17,14 +17,12 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
DECL_BIT(GBATimerFlags, CountUp, 4);
DECL_BIT(GBATimerFlags, DoIrq, 5);
DECL_BIT(GBATimerFlags, Enable, 6);
DECL_BIT(GBATimerFlags, IrqPending, 7);
struct GBA;
struct GBATimer {
uint16_t reload;
int32_t lastEvent;
struct mTimingEvent event;
struct mTimingEvent irq;
GBATimerFlags flags;
};

View File

@ -135,9 +135,7 @@ void ARMReset(struct ARMCore* cpu) {
cpu->executionMode = MODE_THUMB;
_ARMSetMode(cpu, MODE_ARM);
int currentCycles = 0;
ARM_WRITE_PC;
ARMWritePC(cpu);
cpu->cycles = 0;
cpu->nextEvent = 0;
@ -161,12 +159,11 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
cpu->cpsr.priv = MODE_IRQ;
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
cpu->gprs[ARM_PC] = BASE_IRQ;
int currentCycles = 0;
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->cycles += ARMWritePC(cpu);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cycles += currentCycles;
cpu->halted = 0;
}
void ARMRaiseSWI(struct ARMCore* cpu) {
@ -181,12 +178,10 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
cpu->cpsr.priv = MODE_SUPERVISOR;
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
cpu->gprs[ARM_PC] = BASE_SWI;
int currentCycles = 0;
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->cycles += ARMWritePC(cpu);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cycles += currentCycles;
}
void ARMRaiseUndefined(struct ARMCore* cpu) {
@ -201,12 +196,10 @@ void ARMRaiseUndefined(struct ARMCore* cpu) {
cpu->cpsr.priv = MODE_UNDEFINED;
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
cpu->gprs[ARM_PC] = BASE_UNDEF;
int currentCycles = 0;
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->cycles += ARMWritePC(cpu);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cycles += currentCycles;
}
static inline void ARMStep(struct ARMCore* cpu) {

View File

@ -347,11 +347,10 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32
}
if (strcmp(name, "pc") == 0) {
cpu->gprs[ARM_PC] = value;
int32_t currentCycles = 0;
if (cpu->executionMode == MODE_ARM) {
ARM_WRITE_PC;
ARMWritePC(cpu);
} else {
THUMB_WRITE_PC;
ThumbWritePC(cpu);
}
return true;
}
@ -363,11 +362,10 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32
}
cpu->gprs[reg] = value;
if (reg == ARM_PC) {
int32_t currentCycles = 0;
if (cpu->executionMode == MODE_ARM) {
ARM_WRITE_PC;
ARMWritePC(cpu);
} else {
THUMB_WRITE_PC;
ThumbWritePC(cpu);
}
}
return true;

View File

@ -253,7 +253,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
#define ADDR_MODE_2_WRITEBACK(ADDR) \
cpu->gprs[rn] = ADDR; \
if (UNLIKELY(rn == ARM_PC)) { \
ARM_WRITE_PC; \
currentCycles += ARMWritePC(cpu); \
}
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
@ -278,7 +278,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
#define ARM_LOAD_POST_BODY \
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \
if (rd == ARM_PC) { \
ARM_WRITE_PC; \
currentCycles += ARMWritePC(cpu); \
}
#define ARM_STORE_POST_BODY \
@ -301,9 +301,9 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
S_BODY; \
if (rd == ARM_PC) { \
if (cpu->executionMode == MODE_ARM) { \
ARM_WRITE_PC; \
currentCycles += ARMWritePC(cpu); \
} else { \
THUMB_WRITE_PC; \
currentCycles += ThumbWritePC(cpu); \
} \
})
@ -594,7 +594,7 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
load,
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
if (rs & 0x8000) {
ARM_WRITE_PC;
currentCycles += ARMWritePC(cpu);
})
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(STM,
@ -625,22 +625,22 @@ DEFINE_INSTRUCTION_ARM(B,
int32_t offset = opcode << 8;
offset >>= 6;
cpu->gprs[ARM_PC] += offset;
ARM_WRITE_PC;)
currentCycles += ARMWritePC(cpu);)
DEFINE_INSTRUCTION_ARM(BL,
int32_t immediate = (opcode & 0x00FFFFFF) << 8;
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
cpu->gprs[ARM_PC] += immediate >> 6;
ARM_WRITE_PC;)
currentCycles += ARMWritePC(cpu);)
DEFINE_INSTRUCTION_ARM(BX,
int rm = opcode & 0x0000000F;
_ARMSetMode(cpu, cpu->gprs[rm] & 0x00000001);
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
if (cpu->executionMode == MODE_THUMB) {
THUMB_WRITE_PC;
currentCycles += ThumbWritePC(cpu);
} else {
ARM_WRITE_PC;
currentCycles += ARMWritePC(cpu);
})
// End branch definitions

View File

@ -238,14 +238,14 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(MVN, cpu->gprs[rd] = ~cpu->gprs[rn]; THUMB_
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(ADD4,
cpu->gprs[rd] += cpu->gprs[rm];
if (rd == ARM_PC) {
THUMB_WRITE_PC;
currentCycles += ThumbWritePC(cpu);
})
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(CMP3, int32_t aluOut = cpu->gprs[rd] - cpu->gprs[rm]; THUMB_SUBTRACTION_S(cpu->gprs[rd], cpu->gprs[rm], aluOut))
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(MOV3,
cpu->gprs[rd] = cpu->gprs[rm];
if (rd == ARM_PC) {
THUMB_WRITE_PC;
currentCycles += ThumbWritePC(cpu);
})
#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \
@ -310,7 +310,7 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(STMIA,
if (ARM_COND_ ## COND) { \
int8_t immediate = opcode; \
cpu->gprs[ARM_PC] += (int32_t) immediate << 1; \
THUMB_WRITE_PC; \
currentCycles += ThumbWritePC(cpu); \
})
DEFINE_CONDITIONAL_BRANCH_THUMB(EQ)
@ -346,7 +346,7 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPR,
rs |= 1 << ARM_PC,
THUMB_LOAD_POST_BODY;
cpu->gprs[ARM_SP] = address;
THUMB_WRITE_PC;)
currentCycles += ThumbWritePC(cpu);)
DEFINE_LOAD_STORE_MULTIPLE_THUMB(PUSH,
ARM_SP,
@ -369,7 +369,7 @@ DEFINE_INSTRUCTION_THUMB(BKPT, cpu->irqh.bkpt16(cpu, opcode & 0xFF);)
DEFINE_INSTRUCTION_THUMB(B,
int16_t immediate = (opcode & 0x07FF) << 5;
cpu->gprs[ARM_PC] += (((int32_t) immediate) >> 4);
THUMB_WRITE_PC;)
currentCycles += ThumbWritePC(cpu);)
DEFINE_INSTRUCTION_THUMB(BL1,
int16_t immediate = (opcode & 0x07FF) << 5;
@ -380,7 +380,7 @@ DEFINE_INSTRUCTION_THUMB(BL2,
uint32_t pc = cpu->gprs[ARM_PC];
cpu->gprs[ARM_PC] = cpu->gprs[ARM_LR] + immediate;
cpu->gprs[ARM_LR] = pc - 1;
THUMB_WRITE_PC;)
currentCycles += ThumbWritePC(cpu);)
DEFINE_INSTRUCTION_THUMB(BX,
int rm = (opcode >> 3) & 0xF;
@ -391,9 +391,9 @@ DEFINE_INSTRUCTION_THUMB(BX,
}
cpu->gprs[ARM_PC] = (cpu->gprs[rm] & 0xFFFFFFFE) - misalign;
if (cpu->executionMode == MODE_THUMB) {
THUMB_WRITE_PC;
currentCycles += ThumbWritePC(cpu);
} else {
ARM_WRITE_PC;
currentCycles += ARMWritePC(cpu);
})
DEFINE_INSTRUCTION_THUMB(SWI, cpu->irqh.swi16(cpu, opcode & 0xFF))

View File

@ -133,13 +133,13 @@ void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels)
}
bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) {
int value = (int) TableLookup(&filter->levels, category);
int value = (intptr_t) TableLookup(&filter->levels, category);
if (value) {
return value & level;
}
const char* cat = mLogCategoryId(category);
if (cat) {
value = (int) HashTableLookup(&filter->categories, cat);
value = (intptr_t) HashTableLookup(&filter->categories, cat);
if (value) {
TableInsert(&filter->levels, category, (void*)(intptr_t) value);
return value & level;

View File

@ -198,7 +198,7 @@ static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, un
if (tileId >= tiles) {
abort();
}
if (paletteId >= 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
if (paletteId >= 1U << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
abort();
}
#endif

View File

@ -66,8 +66,8 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "break", _setBreakpoint, "Is", "Set a breakpoint" },
{ "c", _continue, "", "Continue execution" },
{ "continue", _continue, "", "Continue execution" },
{ "d", _clearBreakpoint, "I", "Delete a breakpoint" },
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint" },
{ "d", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
{ "dis", _disassemble, "Ii", "Disassemble instructions" },
{ "disasm", _disassemble, "Ii", "Disassemble instructions" },
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" },
@ -841,9 +841,9 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
case DEBUGGER_ENTER_WATCHPOINT:
if (info) {
if (info->type.wp.accessType & WATCHPOINT_WRITE) {
cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08x, old value = 0x%08X)\n", info->address, info->type.wp.newValue, info->type.wp.oldValue);
cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08X, old value = 0x%08X)\n", info->address, info->type.wp.newValue, info->type.wp.oldValue);
} else {
cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08x)\n", info->address, info->type.wp.oldValue);
cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08X)\n", info->address, info->type.wp.oldValue);
}
} else {
cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint\n");

View File

@ -305,11 +305,10 @@ static void _writeGPRs(struct GDBStub* stub, const char* message) {
cpu->gprs[r] = _hex2int(readAddress, 8);
readAddress += 8;
}
int32_t currentCycles = 0;
if (cpu->executionMode == MODE_ARM) {
ARM_WRITE_PC;
ARMWritePC(cpu);
} else {
THUMB_WRITE_PC;
ThumbWritePC(cpu);
}
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
@ -369,11 +368,10 @@ static void _writeRegister(struct GDBStub* stub, const char* message) {
if (reg <= ARM_PC) {
cpu->gprs[reg] = value;
if (reg == ARM_PC) {
int32_t currentCycles = 0;
if (cpu->executionMode == MODE_ARM) {
ARM_WRITE_PC;
ARMWritePC(cpu);
} else {
THUMB_WRITE_PC;
ThumbWritePC(cpu);
}
}
} else if (reg == 0x19) {

View File

@ -99,6 +99,7 @@ static bool _GBCoreInit(struct mCore* core) {
}
core->cpu = cpu;
core->board = gb;
core->timing = &gb->timing;
gbcore->overrides = NULL;
gbcore->debuggerPlatform = NULL;
gbcore->cheatDevice = NULL;

View File

@ -188,11 +188,6 @@ void GBMBCInit(struct GB* gb) {
case 2:
case 3:
gb->memory.mbcType = GB_MBC1;
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
gb->memory.mbcState.mbc1.multicartStride = 4;
} else {
gb->memory.mbcState.mbc1.multicartStride = 5;
}
break;
case 5:
case 6:
@ -255,6 +250,11 @@ void GBMBCInit(struct GB* gb) {
break;
case GB_MBC1:
gb->memory.mbcWrite = _GBMBC1;
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
gb->memory.mbcState.mbc1.multicartStride = 4;
} else {
gb->memory.mbcState.mbc1.multicartStride = 5;
}
break;
case GB_MBC2:
gb->memory.mbcWrite = _GBMBC2;

View File

@ -177,6 +177,7 @@ void GBMemoryReset(struct GB* gb) {
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
GBMBCInit(gb);
switch (gb->memory.mbcType) {
case GB_MBC1:
@ -192,8 +193,9 @@ void GBMemoryReset(struct GB* gb) {
case GB_MMM01:
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
break;
default:
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
break;
}
gb->memory.sramBank = gb->memory.sram;

View File

@ -30,7 +30,7 @@ static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) {
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3));
}
}
int timingFactor = 0x3FF >> !timer->p->doubleSpeed;
unsigned timingFactor = 0x3FF >> !timer->p->doubleSpeed;
if ((timer->internalDiv & timingFactor) == timingFactor) {
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
}
@ -80,7 +80,6 @@ void GBTimerDivReset(struct GBTimer* timer) {
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3));
}
}
int timingFactor = 0x200 >> !timer->p->doubleSpeed;
if (timer->internalDiv & 0x200) {
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
}

View File

@ -43,8 +43,7 @@ static void _SoftReset(struct GBA* gba) {
cpu->gprs[ARM_PC] = BASE_CART0;
}
_ARMSetMode(cpu, MODE_ARM);
int currentCycles = 0;
ARM_WRITE_PC;
ARMWritePC(cpu);
}
static void _RegisterRamReset(struct GBA* gba) {

View File

@ -148,6 +148,7 @@ static bool _GBACoreInit(struct mCore* core) {
}
core->cpu = cpu;
core->board = gba;
core->timing = &gba->timing;
core->debugger = NULL;
core->symbolTable = NULL;
gbacore->overrides = NULL;

View File

@ -266,11 +266,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
memory->dmaTransferRegister &= 0xFFFF0000;
memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16;
} else {
if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata);
}
if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) {
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
} else {
if (source) {

View File

@ -25,6 +25,8 @@
#include <mgba-util/elf-read.h>
#endif
#define GBA_IRQ_DELAY 7
mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba");
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
@ -45,6 +47,8 @@ static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
static void GBABreakpoint(struct ARMCore* cpu, int immediate);
static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate);
#ifdef USE_DEBUGGERS
static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode);
static bool _clearSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t opcode);
@ -86,7 +90,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
GBAHardwareInit(&gba->memory.hw, NULL);
gba->springIRQ = 0;
gba->keySource = 0;
gba->rotationSource = 0;
gba->luminanceSource = 0;
@ -116,6 +119,11 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->yankedRomSize = 0;
mTimingInit(&gba->timing, &gba->cpu->cycles, &gba->cpu->nextEvent);
gba->irqEvent.name = "GBA IRQ Event";
gba->irqEvent.callback = _triggerIRQ;
gba->irqEvent.context = gba;
gba->irqEvent.priority = 0;
}
void GBAUnloadROM(struct GBA* gba) {
@ -234,8 +242,7 @@ void GBASkipBIOS(struct GBA* gba) {
}
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
gba->memory.io[REG_POSTFLG >> 1] = 1;
int currentCycles = 0;
ARM_WRITE_PC;
ARMWritePC(cpu);
}
}
@ -247,11 +254,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
gba->bus |= cpu->prefetch[1] << 16;
}
if (gba->springIRQ && !cpu->cpsr.i) {
ARMRaiseIRQ(cpu);
gba->springIRQ = 0;
}
int32_t nextEvent = cpu->nextEvent;
while (cpu->cycles >= nextEvent) {
cpu->nextEvent = INT_MAX;
@ -474,34 +476,17 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
}
void GBAWriteIE(struct GBA* gba, uint16_t value) {
if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBAWriteIME(struct GBA* gba, uint16_t value) {
if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
gba->memory.io[REG_IF >> 1] |= 1 << irq;
if (gba->memory.io[REG_IE >> 1] & 1 << irq) {
gba->cpu->halted = 0;
if (gba->memory.io[REG_IME >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
GBATestIRQ(gba->cpu);
}
void GBATestIRQ(struct ARMCore* cpu) {
struct GBA* gba = (struct GBA*) cpu->master;
if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
gba->springIRQ = gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1];
gba->cpu->nextEvent = gba->cpu->cycles;
if (gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
if (!mTimingIsScheduled(&gba->timing, &gba->irqEvent)) {
mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY);
}
}
}
@ -527,7 +512,7 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
int level = 1 << GBADebugFlagsGetLevel(gba->debugFlags);
level &= 0x1F;
char oolBuf[0x101];
strncpy(oolBuf, gba->debugString, sizeof(gba->debugString));
strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1);
memset(gba->debugString, 0, sizeof(gba->debugString));
oolBuf[0x100] = '\0';
mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf);
@ -857,6 +842,20 @@ void GBATestKeypadIRQ(struct GBA* gba) {
}
}
static void _triggerIRQ(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBA* gba = user;
gba->cpu->halted = 0;
if (!(gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1])) {
return;
}
if (gba->memory.io[REG_IME >> 1] && !gba->cpu->cpsr.i) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) {
size_t immediate;
for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) {

View File

@ -547,15 +547,18 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
GBAAdjustWaitstates(gba, value);
break;
case REG_IE:
GBAWriteIE(gba, value);
break;
gba->memory.io[REG_IE >> 1] = value;
GBATestIRQ(gba->cpu);
return;
case REG_IF:
gba->springIRQ &= ~value;
value = gba->memory.io[REG_IF >> 1] & ~value;
break;
gba->memory.io[REG_IF >> 1] = value;
GBATestIRQ(gba->cpu);
return;
case REG_IME:
GBAWriteIME(gba, value);
break;
gba->memory.io[REG_IME >> 1] = value;
GBATestIRQ(gba->cpu);
return;
case REG_MAX:
// Some bad interrupt libraries will write to this
break;
@ -931,7 +934,6 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload);
STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent);
STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent);
STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq);
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
@ -971,10 +973,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
}
LOAD_32(when, 0, &state->timers[i].nextIrq);
if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].irq, when);
}
LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);

View File

@ -558,7 +558,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
break;
case REGION_CART2_EX:
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
if (memory->savedata.type == SAVEDATA_EEPROM) {
if (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512) {
value = GBASavedataReadEEPROM(&memory->savedata);
} else if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom);

View File

@ -158,10 +158,10 @@ void GBAVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint32_t address,
switch (address) {
case REG_DISPCNT:
GBAVideoCacheWriteDISPCNT(cache, value);
GBAVideoCacheWriteBGCNT(cache, 0, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 0)->context);
GBAVideoCacheWriteBGCNT(cache, 1, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 1)->context);
GBAVideoCacheWriteBGCNT(cache, 2, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 2)->context);
GBAVideoCacheWriteBGCNT(cache, 3, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 3)->context);
GBAVideoCacheWriteBGCNT(cache, 0, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 0)->context);
GBAVideoCacheWriteBGCNT(cache, 1, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 1)->context);
GBAVideoCacheWriteBGCNT(cache, 2, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 2)->context);
GBAVideoCacheWriteBGCNT(cache, 3, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 3)->context);
break;
case REG_BG0CNT:
GBAVideoCacheWriteBGCNT(cache, 0, value);

View File

@ -77,6 +77,9 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
case SAVEDATA_EEPROM:
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
break;
case SAVEDATA_EEPROM512:
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM512);
break;
case SAVEDATA_FORCE_NONE:
case SAVEDATA_AUTODETECT:
break;
@ -127,6 +130,8 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
case SAVEDATA_EEPROM512:
return out->write(out, savedata->data, SIZE_CART_EEPROM512) == SIZE_CART_EEPROM512;
case SAVEDATA_AUTODETECT:
case SAVEDATA_FORCE_NONE:
return true;
@ -152,7 +157,9 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) {
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return (savedata->vf && savedata->vf->size(savedata->vf) == SIZE_CART_EEPROM512) ? SIZE_CART_EEPROM512 : SIZE_CART_EEPROM;
return SIZE_CART_EEPROM;
case SAVEDATA_EEPROM512:
return SIZE_CART_EEPROM512;
case SAVEDATA_FORCE_NONE:
return 0;
case SAVEDATA_AUTODETECT:
@ -218,6 +225,7 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type)
GBASavedataInitFlash(savedata);
break;
case SAVEDATA_EEPROM:
case SAVEDATA_EEPROM512:
GBASavedataInitEEPROM(savedata);
break;
case SAVEDATA_SRAM:
@ -251,6 +259,9 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
end = savedata->vf->size(savedata->vf);
if (end < flashSize) {
savedata->vf->truncate(savedata->vf, flashSize);
} else if (end >= SIZE_CART_FLASH1M) {
flashSize = SIZE_CART_FLASH1M;
savedata->type = SAVEDATA_FLASH1M;
}
savedata->data = savedata->vf->map(savedata->vf, flashSize, savedata->mapMode);
}
@ -263,22 +274,26 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_EEPROM;
} else {
savedata->type = SAVEDATA_EEPROM512;
} else if (savedata->type != SAVEDATA_EEPROM512 && savedata->type != SAVEDATA_EEPROM) {
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
return;
}
int32_t eepromSize = SIZE_CART_EEPROM512;
if (savedata->type == SAVEDATA_EEPROM) {
eepromSize = SIZE_CART_EEPROM;
}
off_t end;
if (!savedata->vf) {
end = 0;
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
} else {
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_EEPROM512) {
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM512);
} else if (end > SIZE_CART_EEPROM512) {
if (end < eepromSize) {
savedata->vf->truncate(savedata->vf, eepromSize);
} else if (end >= SIZE_CART_EEPROM) {
eepromSize = SIZE_CART_EEPROM;
savedata->type = SAVEDATA_EEPROM;
}
savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
}
@ -420,6 +435,10 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
if (size < SIZE_CART_EEPROM512) {
return;
}
if (savedata->type == SAVEDATA_EEPROM) {
return;
}
savedata->type = SAVEDATA_EEPROM;
if (!savedata->vf || savedata->vf->size(savedata->vf) > SIZE_CART_EEPROM512) {
return;
}

View File

@ -15,7 +15,7 @@
#include <fcntl.h>
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000003;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");
@ -62,6 +62,10 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBASerializedMiscFlags miscFlags = 0;
miscFlags = GBASerializedMiscFlagsSetHalted(miscFlags, gba->cpu->halted);
miscFlags = GBASerializedMiscFlagsSetPOSTFLG(miscFlags, gba->memory.io[REG_POSTFLG >> 1] & 1);
if (mTimingIsScheduled(&gba->timing, &gba->irqEvent)) {
miscFlags = GBASerializedMiscFlagsFillIrqPending(miscFlags);
STORE_32(gba->irqEvent.when - mTimingCurrentTime(&gba->timing), 0, &state->nextIrq);
}
STORE_32(miscFlags, 0, &state->miscFlags);
GBAMemorySerialize(&gba->memory, state);
@ -179,6 +183,11 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(miscFlags, 0, &state->miscFlags);
gba->cpu->halted = GBASerializedMiscFlagsGetHalted(miscFlags);
gba->memory.io[REG_POSTFLG >> 1] = GBASerializedMiscFlagsGetPOSTFLG(miscFlags);
if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) {
int32_t when;
LOAD_32(when, 0, &state->nextIrq);
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
}
GBAVideoDeserialize(&gba->video, state);
GBAMemoryDeserialize(&gba->memory, state);

View File

@ -8,44 +8,11 @@
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
#define TIMER_IRQ_DELAY 7
#define TIMER_RELOAD_DELAY 0
#define TIMER_STARTUP_DELAY 2
#define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2))
static void GBATimerIrq(struct GBA* gba, int timerId) {
struct GBATimer* timer = &gba->timers[timerId];
if (GBATimerFlagsIsIrqPending(timer->flags)) {
timer->flags = GBATimerFlagsClearIrqPending(timer->flags);
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
}
}
static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 0);
}
static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 1);
}
static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 2);
}
static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 3);
}
static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
struct GBATimer* timer = &gba->timers[timerId];
if (GBATimerFlagsIsCountUp(timer->flags)) {
@ -55,10 +22,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
}
if (GBATimerFlagsIsDoIrq(timer->flags)) {
timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
if (!mTimingIsScheduled(&gba->timing, &timer->irq)) {
mTimingSchedule(&gba->timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate);
}
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
}
if (gba->audio.enable && timerId < 2) {
@ -120,22 +84,6 @@ void GBATimerInit(struct GBA* gba) {
gba->timers[3].event.callback = GBATimerUpdate3;
gba->timers[3].event.context = gba;
gba->timers[3].event.priority = 0x23;
gba->timers[0].irq.name = "GBA Timer 0 IRQ";
gba->timers[0].irq.callback = GBATimerIrq0;
gba->timers[0].irq.context = gba;
gba->timers[0].irq.priority = 0x28;
gba->timers[1].irq.name = "GBA Timer 1 IRQ";
gba->timers[1].irq.callback = GBATimerIrq1;
gba->timers[1].irq.context = gba;
gba->timers[1].irq.priority = 0x29;
gba->timers[2].irq.name = "GBA Timer 2 IRQ";
gba->timers[2].irq.callback = GBATimerIrq2;
gba->timers[2].irq.context = gba;
gba->timers[2].irq.priority = 0x2A;
gba->timers[3].irq.name = "GBA Timer 3 IRQ";
gba->timers[3].irq.callback = GBATimerIrq3;
gba->timers[3].irq.context = gba;
gba->timers[3].irq.priority = 0x2B;
}
void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {

View File

@ -84,7 +84,8 @@ static enum {
} hasSound;
// TODO: Move into context
static void* outputBuffer;
static color_t* outputBuffer = NULL;
static color_t* screenshotBuffer = NULL;
static struct mAVStream stream;
static int16_t* audioLeft = 0;
static size_t audioPos = 0;
@ -135,6 +136,11 @@ static void _cleanup(void) {
if (outputBuffer) {
linearFree(outputBuffer);
outputBuffer = NULL;
}
if (screenshotBuffer) {
linearFree(screenshotBuffer);
screenshotBuffer = NULL;
}
C3D_RenderTargetDelete(topScreen[0]);
@ -276,7 +282,7 @@ static void _setup(struct mGUIRunner* runner) {
_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L);
_map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R);
outputBuffer = linearMemAlign(256 * 224 * 2, 0x80);
outputBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
unsigned mode;
@ -547,7 +553,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
GSPGPU_FlushDataCache(outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
C3D_SyncDisplayTransfer(
outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
(u32*) outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
@ -564,22 +570,22 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
C3D_Tex* tex = &outputTexture;
color_t* newPixels = linearMemAlign(256 * height * sizeof(color_t), 0x100);
if (!screenshotBuffer) {
screenshotBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
}
unsigned y;
for (y = 0; y < height; ++y) {
memcpy(&newPixels[y * 256], &pixels[y * width], width * sizeof(color_t));
memset(&newPixels[y * 256 + width], 0, (256 - width) * sizeof(color_t));
memcpy(&screenshotBuffer[y * 256], &pixels[y * width], width * sizeof(color_t));
memset(&screenshotBuffer[y * 256 + width], 0, (256 - width) * sizeof(color_t));
}
GSPGPU_FlushDataCache(newPixels, 256 * height * sizeof(u32));
GSPGPU_FlushDataCache(screenshotBuffer, 256 * height * sizeof(color_t));
C3D_SyncDisplayTransfer(
(u32*) newPixels, GX_BUFFER_DIM(256, height),
(u32*) screenshotBuffer, GX_BUFFER_DIM(256, height),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
linearFree(newPixels);
_drawTex(runner->core, faded);
}

View File

@ -25,18 +25,18 @@
#include <vita2d.h>
static void _drawStart(void) {
vita2d_set_vblank_wait(false);
static int vcount = 0;
extern bool frameLimiter;
int oldVCount = vcount;
vcount = sceDisplayGetVcount();
vita2d_set_vblank_wait(frameLimiter && vcount + 1 >= oldVCount);
vita2d_start_drawing();
vita2d_clear_screen();
}
static void _drawEnd(void) {
static int vcount = 0;
extern bool frameLimiter;
int oldVCount = vcount;
vita2d_end_drawing();
vcount = sceDisplayGetVcount();
vita2d_set_vblank_wait(frameLimiter && vcount + 1 >= oldVCount);
vita2d_wait_rendering_done();
vita2d_swap_buffers();
}

View File

@ -35,6 +35,7 @@ void free(void*);
#include "flags.h"
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h>
#include <mgba/core/core.h>
#include <mgba/core/map-cache.h>

View File

@ -20,6 +20,7 @@ ffi.set_source("mgba._pylib", """
#define inline
#include "flags.h"
#define OPAQUE_THREADING
#include <mgba/core/blip_buf.h>
#include <mgba/core/cache-set.h>
#include <mgba-util/common.h>
#include <mgba/core/core.h>

View File

@ -65,7 +65,6 @@ bool mPythonScriptEngineInit(struct mScriptEngine* se, struct mScriptBridge* sb)
}
void mPythonScriptEngineDeinit(struct mScriptEngine* se) {
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
free(se);
}
@ -76,7 +75,7 @@ bool mPythonScriptEngineIsScript(struct mScriptEngine* se, const char* name, str
}
bool mPythonScriptEngineLoadScript(struct mScriptEngine* se, const char* name, struct VFile* vf) {
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
UNUSED(se);
return mPythonLoadScript(name, vf);
}
@ -94,7 +93,7 @@ void mPythonScriptEngineRun(struct mScriptEngine* se) {
}
bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) {
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
UNUSED(se);
return mPythonLookupSymbol(name, out);
}

View File

@ -0,0 +1,57 @@
# Copyright (c) 2013-2018 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/.
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
class Buffer(object):
def __init__(self, native, internal_rate):
self._native = native
self._internal_rate = internal_rate
@property
def available(self):
return lib.blip_samples_avail(self._native)
def set_rate(self, rate):
lib.blip_set_rates(self._native, self._internal_rate, rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % samples)
count = self.read_into(buffer, samples, 1, 0)
return buffer[:count]
def read_into(self, buffer, samples, channels=1, interleave=0):
return lib.blip_read_samples(self._native, ffi.addressof(buffer, interleave), samples, channels == 2)
def clear(self):
lib.blip_clear(self._native)
class StereoBuffer(object):
def __init__(self, left, right):
self._left = left
self._right = right
@property
def available(self):
return min(self._left.available, self._right.available)
def set_rate(self, rate):
self._left.set_rate(rate)
self._right.set_rate(rate)
def read(self, samples):
buffer = ffi.new("short[%i]" % (2 * samples))
count = self.read_into(buffer, samples)
return buffer[0:2 * count]
def read_into(self, buffer, samples):
samples = self._left.read_into(buffer, samples, 2, 0)
return self._right.read_into(buffer, samples, 2, 1)
def clear(self):
self._left.clear()
self._right.clear()

View File

@ -4,7 +4,7 @@
# 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/.
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
from . import tile
from . import tile, audio
from cached_property import cached_property
from functools import wraps
@ -167,51 +167,81 @@ class Core(object):
def _load(self):
self._was_reset = True
@protected
def load_file(self, path):
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
def is_rom(self, vfile):
return bool(self._core.isROM(vfile.handle))
@protected
def load_rom(self, vfile):
return bool(self._core.loadROM(self._core, vfile.handle))
@protected
def load_bios(self, vfile, id=0):
return bool(self._core.loadBIOS(self._core, vfile.handle, id))
@protected
def load_save(self, vfile):
return bool(self._core.loadSave(self._core, vfile.handle))
@protected
def load_temporary_save(self, vfile):
return bool(self._core.loadTemporarySave(self._core, vfile.handle))
@protected
def load_patch(self, vfile):
return bool(self._core.loadPatch(self._core, vfile.handle))
@protected
def load_config(self, config):
lib.mCoreLoadForeignConfig(self._core, config._native)
@protected
def autoload_save(self):
return bool(lib.mCoreAutoloadSave(self._core))
@protected
def autoload_patch(self):
return bool(lib.mCoreAutoloadPatch(self._core))
@protected
def autoload_cheats(self):
return bool(lib.mCoreAutoloadCheats(self._core))
@property
def platform(self):
return self._core.platform(self._core)
@protected
def desired_video_dimensions(self):
width = ffi.new("unsigned*")
height = ffi.new("unsigned*")
self._core.desiredVideoDimensions(self._core, width, height)
return width[0], height[0]
@protected
def set_video_buffer(self, image):
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
@protected
def set_audio_buffer_size(self, size):
self._core.setAudioBufferSize(self._core, size)
@property
def audio_buffer_size(self):
return self._core.getAudioBufferSize(self._core)
@protected
def get_audio_channels(self):
return audio.StereoBuffer(self.get_audio_channel(0), self.get_audio_channel(1));
@protected
def get_audio_channel(self, channel):
return audio.Buffer(self._core.getAudioChannel(self._core, channel), self.frequency)
@protected
def reset(self):
self._core.reset(self._core)
self._load()
@ -227,6 +257,7 @@ class Core(object):
self._core.runLoop(self._core)
@needs_reset
@protected
def step(self):
self._core.step(self._core)

View File

@ -11,10 +11,10 @@ except ImportError:
def search(core):
crc32 = None
if hasattr(core, 'PLATFORM_GBA') and core.platform() == core.PLATFORM_GBA:
if hasattr(core, 'PLATFORM_GBA') and core.platform == core.PLATFORM_GBA:
platform = 'GBA'
crc32 = core.crc32
if hasattr(core, 'PLATFORM_GB') and core.platform() == core.PLATFORM_GB:
if hasattr(core, 'PLATFORM_GB') and core.platform == core.PLATFORM_GB:
platform = 'GB'
crc32 = core.crc32
cls = mgba_gamedata.registry.search(platform, {'crc32': crc32})

View File

@ -17,6 +17,8 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
{
m_ui.setupUi(this);
m_ui.prompt->installEventFilter(this);
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log);
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach);
@ -36,7 +38,47 @@ void DebuggerConsole::postLine() {
if (line.isEmpty()) {
m_consoleController->enterLine(QString("\n"));
} else {
m_history.append(line);
m_historyOffset = 0;
log(QString("> %1\n").arg(line));
m_consoleController->enterLine(line);
}
}
bool DebuggerConsole::eventFilter(QObject*, QEvent* event) {
if (event->type() != QEvent::KeyPress) {
return false;
}
if (m_history.isEmpty()) {
return false;
}
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
switch (keyEvent->key()) {
case Qt::Key_Down:
if (m_historyOffset <= 0) {
return false;
}
--m_historyOffset;
break;
case Qt::Key_Up:
if (m_historyOffset >= m_history.size()) {
return false;
}
++m_historyOffset;
break;
case Qt::Key_End:
m_historyOffset = 0;
break;
case Qt::Key_Home:
m_historyOffset = m_history.size();
break;
default:
return false;
}
if (m_historyOffset == 0) {
m_ui.prompt->clear();
} else {
m_ui.prompt->setText(m_history[m_history.size() - m_historyOffset]);
}
return true;
}

View File

@ -21,8 +21,13 @@ private slots:
void log(const QString&);
void postLine();
protected:
bool eventFilter(QObject*, QEvent*) override;
private:
Ui::DebuggerConsole m_ui;
QStringList m_history;
int m_historyOffset;
DebuggerConsoleController* m_consoleController;
};

View File

@ -193,8 +193,15 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
mGLES2Context* gl2Backend;
#endif
m_gl->makeCurrent();
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
#if !defined(_WIN32) || defined(USE_EPOXY)
if (majorVersion >= 2) {
if (extensions.contains("GL_ARB_framebuffer_object") && majorVersion >= 2) {
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
mGLES2ContextCreate(gl2Backend);
m_backend = &gl2Backend->d;
@ -215,10 +222,6 @@ PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
painter->m_gl->swapBuffers();
};
m_gl->makeCurrent();
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
m_backend->init(m_backend, reinterpret_cast<WHandle>(m_gl->winId()));
#if !defined(_WIN32) || defined(USE_EPOXY)
if (m_supportsShaders) {

View File

@ -278,13 +278,13 @@ void GBAKeyEditor::lookupAxes(const mInputMap* map) {
if (description->highDirection != GBA_KEY_NONE) {
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
if (key) {
key->setValueAxis(axis, description->deadHigh);
key->setValueAxis(axis, GamepadAxisEvent::POSITIVE);
}
}
if (description->lowDirection != GBA_KEY_NONE) {
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
if (key) {
key->setValueAxis(axis, description->deadLow);
key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE);
}
}
}, this);
@ -350,14 +350,6 @@ bool GBAKeyEditor::findFocus(KeyEditor* needle) {
}
#ifdef BUILD_SDL
void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
if (!findFocus()) {
return;
}
KeyEditor* focused = *m_currentKey;
focused->setValueAxis(axis, value);
}
void GBAKeyEditor::selectGamepad(int index) {
m_controller->setGamepad(m_type, index);
m_profile = m_profileSelect->currentText();

View File

@ -42,7 +42,6 @@ private slots:
void setNext();
void refresh();
#ifdef BUILD_SDL
void setAxisValue(int axis, int32_t value);
void selectGamepad(int index);
void updateJoysticks();
#endif

View File

@ -48,10 +48,10 @@ void KeyEditor::setValueButton(int button) {
setValue(button);
}
void KeyEditor::setValueAxis(int axis, int32_t value) {
void KeyEditor::setValueAxis(int axis, GamepadAxisEvent::Direction direction) {
m_button = true;
m_axis = axis;
m_direction = value < 0 ? GamepadAxisEvent::NEGATIVE : GamepadAxisEvent::POSITIVE;
m_direction = direction;
updateButtonText();
emit axisChanged(axis, m_direction);
}

View File

@ -33,7 +33,7 @@ public slots:
void setValue(int key);
void setValueKey(int key);
void setValueButton(int button);
void setValueAxis(int axis, int32_t value);
void setValueAxis(int axis, GamepadAxisEvent::Direction value);
void setValueHat(int hat, GamepadHatEvent::Direction value);
void clearButton();
void clearAxis();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>565</width>
<height>658</height>
<width>822</width>
<height>886</height>
</rect>
</property>
<property name="windowTitle">
@ -98,7 +98,7 @@
<item>
<widget class="QRadioButton" name="width8">
<property name="text">
<string>1 Byte</string>
<string>&amp;1 Byte</string>
</property>
<property name="checked">
<bool>true</bool>
@ -121,7 +121,7 @@
<item>
<widget class="QRadioButton" name="width16">
<property name="text">
<string>2 Bytes</string>
<string>&amp;2 Bytes</string>
</property>
</widget>
</item>
@ -141,7 +141,7 @@
<item>
<widget class="QRadioButton" name="width32">
<property name="text">
<string>4 Bytes</string>
<string>&amp;4 Bytes</string>
</property>
</widget>
</item>
@ -168,7 +168,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<zorder></zorder>
</widget>
</item>
<item>

View File

@ -59,8 +59,12 @@ void TilePainter::setTileCount(int tiles) {
int w = width() / m_size;
int h = (tiles + w - 1) * m_size / w;
setMinimumSize(m_size, h - (h % m_size));
resizeEvent(nullptr);
} else {
int w = minimumSize().width() / m_size;
int h = (tiles + w - 1) * m_size / w;
setMinimumSize(minimumSize().width(), h - (h % m_size));
}
resizeEvent(nullptr);
}
void TilePainter::setTileMagnification(int mag) {

View File

@ -65,6 +65,21 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
updateTiles(true);
});
connect(m_ui.tilesPerRow, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int count) {
m_ui.tiles->setMinimumSize(m_ui.magnification->value() * 8 * count, m_ui.tiles->minimumSize().height());
updateTiles(true);
});
connect(m_ui.tileFit, &QAbstractButton::toggled, [this](bool selected) {
if (!selected) {
m_ui.tiles->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_ui.tiles->setMinimumSize(m_ui.magnification->value() * 8 * m_ui.tilesPerRow->value(), m_ui.tiles->minimumSize().height());
} else {
m_ui.tiles->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
}
updateTiles(true);
});
}
#ifdef M_CORE_GBA

View File

@ -6,48 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>501</width>
<height>335</height>
<width>693</width>
<height>467</height>
</rect>
</property>
<property name="windowTitle">
<string>Tiles</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1" rowspan="5">
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item row="0" column="1" rowspan="4">
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
@ -66,7 +33,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<width>405</width>
<height>768</height>
</rect>
</property>
@ -112,7 +79,7 @@
</widget>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -125,22 +92,84 @@
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QSpinBox" name="paletteId">
<property name="maximum">
<number>15</number>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QCheckBox" name="palette256">
<property name="text">
<string>256 colors</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="tilesPerRow">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>32</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Tiles per row</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="tileFit">
<property name="text">
<string>Fit to window</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -164,6 +193,22 @@
</customwidgets>
<resources/>
<connections>
<connection>
<sender>tileFit</sender>
<signal>toggled(bool)</signal>
<receiver>tilesPerRow</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>162</x>
<y>180</y>
</hint>
<hint type="destinationlabel">
<x>39</x>
<y>133</y>
</hint>
</hints>
</connection>
<connection>
<sender>magnification</sender>
<signal>valueChanged(int)</signal>
@ -171,12 +216,12 @@
<slot>setTileMagnification(int)</slot>
<hints>
<hint type="sourcelabel">
<x>36</x>
<y>83</y>
<x>39</x>
<y>81</y>
</hint>
<hint type="destinationlabel">
<x>339</x>
<y>396</y>
<x>462</x>
<y>391</y>
</hint>
</hints>
</connection>
@ -187,11 +232,11 @@
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>158</x>
<y>29</y>
<x>148</x>
<y>24</y>
</hint>
<hint type="destinationlabel">
<x>44</x>
<x>39</x>
<y>29</y>
</hint>
</hints>

View File

@ -707,6 +707,7 @@ void Window::gameStarted() {
}
attachWidget(m_display.get());
m_display->setMinimumSize(size);
setFocus();
#ifndef Q_OS_MAC
if (isFullScreen()) {

View File

@ -4624,20 +4624,30 @@ wenn vorhanden</translation>
<translation>Tiles</translation>
</message>
<message>
<location filename="../TileView.ui" line="140"/>
<location filename="../TileView.ui" line="110"/>
<source>256 colors</source>
<translation>256 Farben</translation>
</message>
<message>
<location filename="../TileView.ui" line="31"/>
<location filename="../TileView.ui" line="123"/>
<source>×</source>
<translation>×</translation>
</message>
<message>
<location filename="../TileView.ui" line="44"/>
<location filename="../TileView.ui" line="136"/>
<source>Magnification</source>
<translation>Vergrößerung</translation>
</message>
<message>
<location filename="../TileView.ui" line="159"/>
<source>Tiles per row</source>
<translation>Tiles pro Zeile</translation>
</message>
<message>
<location filename="../TileView.ui" line="166"/>
<source>Fit to window</source>
<translation>An Fenster anpassen</translation>
</message>
</context>
<context>
<name>VideoView</name>

View File

@ -304,7 +304,7 @@ void mSDLPlayerSaveConfig(const struct mSDLPlayer* context, struct Configuration
#else
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
#endif
char value[12];
char value[16];
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "tiltAxisX", value, name);
snprintf(value, sizeof(value), "%i", context->rotation.axisY);

View File

@ -2,9 +2,9 @@ if(NOT PROJECT_NAME)
set(PROJECT_NAME "mGBA")
endif()
set(LIB_VERSION_MAJOR 0)
set(LIB_VERSION_MINOR 7)
set(LIB_VERSION_MINOR 8)
set(LIB_VERSION_PATCH 0)
set(LIB_VERSION_ABI 0.7)
set(LIB_VERSION_ABI 0.8)
set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH})
set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")