mirror of
https://github.com/libretro/mgba.git
synced 2024-11-23 16:10:01 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0a79981bfa
17
CHANGES
17
CHANGES
@ -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
|
||||
|
@ -44,6 +44,7 @@ struct mVideoLogContext;
|
||||
struct mCore {
|
||||
void* cpu;
|
||||
void* board;
|
||||
struct mTiming* timing;
|
||||
struct mDebugger* debugger;
|
||||
struct mDebuggerSymbols* symbolTable;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
10
src/gb/mbc.c
10
src/gb/mbc.c
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
20
src/gba/io.c
20
src/gba/io.c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
57
src/platform/python/mgba/audio.py
Normal file
57
src/platform/python/mgba/audio.py
Normal 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()
|
@ -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)
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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>&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>&2 Bytes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -141,7 +141,7 @@
|
||||
<item>
|
||||
<widget class="QRadioButton" name="width32">
|
||||
<property name="text">
|
||||
<string>4 Bytes</string>
|
||||
<string>&4 Bytes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -168,7 +168,6 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -707,6 +707,7 @@ void Window::gameStarted() {
|
||||
}
|
||||
attachWidget(m_display.get());
|
||||
m_display->setMinimumSize(size);
|
||||
setFocus();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
if (isFullScreen()) {
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user