mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
9ff5ef9d04
|
@ -9,7 +9,7 @@ cache:
|
|||
install:
|
||||
- git -C C:\Tools\vcpkg pull --quiet
|
||||
- C:\Tools\vcpkg\bootstrap-vcpkg
|
||||
- vcpkg --triplet x64-windows install ffmpeg libepoxy libpng libzip sdl2 sqlite3
|
||||
- vcpkg --triplet x64-windows --recurse install ffmpeg libepoxy libpng libzip sdl2 sqlite3
|
||||
- vcpkg --no-dry-run upgrade
|
||||
- rd /Q /S C:\Tools\vcpkg\buildtrees
|
||||
before_build:
|
||||
|
|
8
CHANGES
8
CHANGES
|
@ -44,9 +44,13 @@ Emulation fixes:
|
|||
- ARM: Fix ALU reading PC after shifting
|
||||
- ARM: Fix STR storing PC after address calculation
|
||||
- GB: Partially fix timing for skipped BIOS
|
||||
- GB Audio: Fix initial sweep state
|
||||
- GB Audio: Fix serializing sweep time
|
||||
- GB Audio: Fix deserializing audio channels 2 and 3
|
||||
- GB MBC: Fix MBC1 mode changing behavior
|
||||
- GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716)
|
||||
- GBA: Fix timing advancing too quickly in rare cases
|
||||
- GBA Audio: Fix deserializing SOUNDCNT_L
|
||||
- GBA BIOS: Implement dummy sound driver calls
|
||||
- GBA BIOS: Improve HLE BIOS timing
|
||||
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
|
||||
|
@ -56,6 +60,7 @@ Emulation fixes:
|
|||
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
|
||||
- GBA SIO: Fix copying Normal mode transfer values
|
||||
- GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800)
|
||||
- GBA SIO: Fix deseralizing SIO registers
|
||||
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
|
||||
- GBA Video: Fix Hblank timing
|
||||
- GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846)
|
||||
|
@ -65,9 +70,11 @@ Other fixes:
|
|||
- 3DS: Fix crash with libctru 2.0 when exiting
|
||||
- All: Improve export headers (fixes mgba.io/i/1738)
|
||||
- Core: Ensure ELF regions can be written before trying
|
||||
- Core: Fix reported ROM size when a fixed buffer size is used
|
||||
- Debugger: Don't skip undefined instructions when debugger attached
|
||||
- FFmpeg: Fix some small memory leaks
|
||||
- FFmpeg: Fix encoding of time base
|
||||
- GBA: Disable more checks when loading GS save with checks disabled (fixes mgba.io/i/1851)
|
||||
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
|
||||
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)
|
||||
- Qt: Fix a race condition in the frame inspector
|
||||
|
@ -82,6 +89,7 @@ Misc:
|
|||
- GBA: Allow pausing event loop while CPU is blocked
|
||||
- Debugger: Keep track of global cycle count
|
||||
- FFmpeg: Add looping option for GIF/APNG
|
||||
- mGUI: Show battery percentage
|
||||
- Qt: Renderer can be changed while a game is running
|
||||
- Qt: Add hex index to palette view
|
||||
- Qt: Add transformation matrix info to sprite view
|
||||
|
|
|
@ -42,13 +42,15 @@ enum GUICursorState {
|
|||
|
||||
enum {
|
||||
BATTERY_EMPTY = 0,
|
||||
BATTERY_LOW = 1,
|
||||
BATTERY_HALF = 2,
|
||||
BATTERY_HIGH = 3,
|
||||
BATTERY_FULL = 4,
|
||||
BATTERY_LOW = 25,
|
||||
BATTERY_HALF = 50,
|
||||
BATTERY_HIGH = 75,
|
||||
BATTERY_FULL = 100,
|
||||
BATTERY_VALUE = 0x7F,
|
||||
BATTERY_PERCENTAGE_VALID = 0x80,
|
||||
|
||||
BATTERY_CHARGING = 8,
|
||||
BATTERY_NOT_PRESENT = 16
|
||||
BATTERY_CHARGING = 0x100,
|
||||
BATTERY_NOT_PRESENT = 0x200,
|
||||
};
|
||||
|
||||
struct GUIBackground {
|
||||
|
|
|
@ -35,6 +35,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextE
|
|||
void mTimingDeinit(struct mTiming* timing);
|
||||
void mTimingClear(struct mTiming* timing);
|
||||
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
|
||||
void mTimingScheduleAbsolute(struct mTiming* timing, struct mTimingEvent*, int32_t when);
|
||||
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*);
|
||||
bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*);
|
||||
int32_t mTimingTick(struct mTiming* timing, int32_t cycles);
|
||||
|
|
|
@ -250,6 +250,7 @@ void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info);
|
|||
bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2,
|
||||
struct ARMInstructionInfo* out);
|
||||
int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen);
|
||||
uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -142,4 +142,26 @@ static inline bool ARMTestCondition(struct ARMCore* cpu, unsigned condition) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline enum RegisterBank ARMSelectBank(enum PrivilegeMode mode) {
|
||||
switch (mode) {
|
||||
case MODE_USER:
|
||||
case MODE_SYSTEM:
|
||||
// No banked registers
|
||||
return BANK_NONE;
|
||||
case MODE_FIQ:
|
||||
return BANK_FIQ;
|
||||
case MODE_IRQ:
|
||||
return BANK_IRQ;
|
||||
case MODE_SUPERVISOR:
|
||||
return BANK_SUPERVISOR;
|
||||
case MODE_ABORT:
|
||||
return BANK_ABORT;
|
||||
case MODE_UNDEFINED:
|
||||
return BANK_UNDEFINED;
|
||||
default:
|
||||
// This should be unreached
|
||||
return BANK_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,7 +59,9 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* | bits 21 - 31: Reserved
|
||||
* | 0x0004C - 0x0004F: Next frame
|
||||
* | 0x00050 - 0x00053: Next channel 3 fade
|
||||
* | 0x00054 - 0x00057: Reserved
|
||||
* | 0x00054 - 0x00057: Sweep state
|
||||
* | bits 0 - 2: Timesteps
|
||||
* | bits 3 - 31: Reserved
|
||||
* | 0x00058 - 0x0005B: Next event
|
||||
* 0x0005C - 0x0006B: Audio channel 2 state
|
||||
* | 0x0005C - 0x0005F: Envelepe timing
|
||||
|
@ -87,22 +89,23 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bit 7: Reserved
|
||||
* | 0x000A5: Channel 2 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bits 7: Reserved
|
||||
* | bit 7: Reserved
|
||||
* | 0x000A6: Channel 4 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bits 7: Reserved
|
||||
* | bits 6 - 7: Current frame (continued)
|
||||
* | 0x000A7: Miscellaneous audio flags
|
||||
* | bits 0 - 3: Current frame
|
||||
* | bit 4: Is channel 1 sweep enabled?
|
||||
* | bit 5: Has channel 1 sweep occurred?
|
||||
* | bit 6: Is channel 3's memory readable?
|
||||
* | bit 7: Reserved
|
||||
* | bit 0: Current frame (continuation)
|
||||
* | bit 1: Is channel 1 sweep enabled?
|
||||
* | bit 2: Has channel 1 sweep occurred?
|
||||
* | bit 3: Is channel 3's memory readable?
|
||||
* | bit 4: Skip frame
|
||||
* | bits 5 - 7: Reserved
|
||||
* | 0x000A8 - 0x000AB: Left capacitor charge
|
||||
* | 0x000AC - 0x000AF: Right capacitor charge
|
||||
* | 0x000B0 - 0x000B3: Next sample
|
||||
|
@ -203,12 +206,16 @@ DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7);
|
|||
DECL_BITS(GBSerializedAudioEnvelope, NextStep, 7, 3);
|
||||
DECL_BITS(GBSerializedAudioEnvelope, Frequency, 10, 11);
|
||||
|
||||
|
||||
DECL_BITFIELD(GBSerializedAudioSweep, uint32_t);
|
||||
DECL_BITS(GBSerializedAudioSweep, Time, 0, 3);
|
||||
|
||||
struct GBSerializedPSGState {
|
||||
struct {
|
||||
GBSerializedAudioEnvelope envelope;
|
||||
int32_t nextFrame;
|
||||
int32_t nextCh3Fade;
|
||||
int32_t reserved;
|
||||
GBSerializedAudioSweep sweep;
|
||||
uint32_t nextEvent;
|
||||
} ch1;
|
||||
struct {
|
||||
|
|
|
@ -41,7 +41,10 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bits 10 - 20: Shadow frequency register
|
||||
* | bits 21 - 31: Reserved
|
||||
* | 0x00134 - 0x00137: Next frame
|
||||
* | 0x00138 - 0x0013F: Reserved
|
||||
* | 0x00138 - 0x0013B: Next channel 3 fade
|
||||
* | 0x0013C - 0x0013F: Sweep state
|
||||
* | bits 0 - 2: Timesteps
|
||||
* | bits 3 - 7: Reserved
|
||||
* | 0x00140 - 0x00143: Next event
|
||||
* 0x00144 - 0x00153: Audio channel 2 state
|
||||
* | 0x00144 - 0x00147: Envelepe timing
|
||||
|
@ -75,21 +78,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bit 7: Reserved
|
||||
* | 0x001DD - 0x001DD: Channel 2 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bits 7: Reserved
|
||||
* | bit 7: Reserved
|
||||
* | 0x001DE - 0x001DE: Channel 4 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
* | bits 4 - 5: Is dead?
|
||||
* | bit 6: Is high?
|
||||
* | bits 7: Reserved
|
||||
* | bits 6 - 7: Current frame (continued)
|
||||
* | 0x001DF - 0x001DF: Miscellaneous audio flags
|
||||
* | bits 0 - 3: Current frame
|
||||
* | bit 4: Is channel 1 sweep enabled?
|
||||
* | bit 5: Has channel 1 sweep occurred?
|
||||
* | bits 6 - 7: Reserved
|
||||
* | bit 0: Current frame (continuation)
|
||||
* | bit 1: Is channel 1 sweep enabled?
|
||||
* | bit 2: Has channel 1 sweep occurred?
|
||||
* | bit 3: Is channel 3's memory readable?
|
||||
* | bit 4: Skip frame
|
||||
* | bits 5 - 7: Reserved
|
||||
* 0x001E0 - 0x001FF: Video miscellaneous state
|
||||
* | 0x001E0 - 0x001E3: Next event
|
||||
* | 0x001E4 - 0x001F7: Reserved
|
||||
|
|
|
@ -9,16 +9,14 @@
|
|||
#include <mgba/internal/arm/isa-inlines.h>
|
||||
#include <mgba/internal/arm/isa-thumb.h>
|
||||
|
||||
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode);
|
||||
|
||||
void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
|
||||
if (mode == cpu->privilegeMode) {
|
||||
// Not switching modes after all
|
||||
return;
|
||||
}
|
||||
|
||||
enum RegisterBank newBank = _ARMSelectBank(mode);
|
||||
enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode);
|
||||
enum RegisterBank newBank = ARMSelectBank(mode);
|
||||
enum RegisterBank oldBank = ARMSelectBank(cpu->privilegeMode);
|
||||
if (newBank != oldBank) {
|
||||
// Switch banked registers
|
||||
if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) {
|
||||
|
@ -46,28 +44,6 @@ void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
|
|||
cpu->privilegeMode = mode;
|
||||
}
|
||||
|
||||
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
|
||||
switch (mode) {
|
||||
case MODE_USER:
|
||||
case MODE_SYSTEM:
|
||||
// No banked registers
|
||||
return BANK_NONE;
|
||||
case MODE_FIQ:
|
||||
return BANK_FIQ;
|
||||
case MODE_IRQ:
|
||||
return BANK_IRQ;
|
||||
case MODE_SUPERVISOR:
|
||||
return BANK_SUPERVISOR;
|
||||
case MODE_ABORT:
|
||||
return BANK_ABORT;
|
||||
case MODE_UNDEFINED:
|
||||
return BANK_UNDEFINED;
|
||||
default:
|
||||
// This should be unreached
|
||||
return BANK_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void ARMInit(struct ARMCore* cpu) {
|
||||
memset(&cpu->cp15, 0, sizeof(cpu->cp15));
|
||||
cpu->master->init(cpu, cpu->master);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
#include <mgba-util/math.h>
|
||||
|
||||
#define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv
|
||||
|
||||
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
||||
|
||||
static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) {
|
||||
|
@ -36,7 +38,8 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
struct mStackTrace* stack = &d->p->stackTrace;
|
||||
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]) {
|
||||
enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv);
|
||||
if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
|
||||
// The stack frame has been popped off the stack. This means the function
|
||||
// has been returned from, or that the stack pointer has been otherwise
|
||||
// manipulated. Either way, the function is done executing.
|
||||
|
@ -45,7 +48,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
shouldBreak = shouldBreak || frame->breakWhenFinished;
|
||||
mStackTracePop(stack);
|
||||
frame = mStackTraceGetFrame(stack, 0);
|
||||
} while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]);
|
||||
} while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame)));
|
||||
if (shouldBreak) {
|
||||
struct mDebuggerEntryInfo debuggerInfo = {
|
||||
.address = pc,
|
||||
|
@ -83,10 +86,10 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
return false;
|
||||
}
|
||||
|
||||
bool isCall = interrupt || (info.branchType & ARM_BRANCH_LINKED);
|
||||
bool isCall = info.branchType & ARM_BRANCH_LINKED;
|
||||
uint32_t destAddress;
|
||||
|
||||
if (interrupt && info.branchType == ARM_BRANCH_NONE) {
|
||||
if (interrupt && !isCall) {
|
||||
// The stack frame was already pushed up above, so there's no
|
||||
// action necessary here, but we still want to check for a
|
||||
// breakpoint down below.
|
||||
|
@ -114,6 +117,10 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
bool isBranch = ARMInstructionIsBranch(info.mnemonic);
|
||||
int reg = (isBranch ? info.op1.reg : info.op2.reg);
|
||||
destAddress = cpu->gprs[reg];
|
||||
if (!isBranch && (info.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) {
|
||||
uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc);
|
||||
destAddress = cpu->memory.load32(cpu, ptrAddress, NULL);
|
||||
}
|
||||
if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) {
|
||||
// ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose.
|
||||
struct ARMInstructionInfo prevInfo;
|
||||
|
@ -144,25 +151,23 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin
|
|||
return false;
|
||||
}
|
||||
|
||||
if (info.branchType & ARM_BRANCH_INDIRECT) {
|
||||
destAddress = cpu->memory.load32(cpu, destAddress, NULL);
|
||||
}
|
||||
|
||||
if (isCall) {
|
||||
int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB;
|
||||
frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
|
||||
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mStackTracePop(stack);
|
||||
} else if (!interrupt) {
|
||||
if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) {
|
||||
mStackTracePop(stack);
|
||||
}
|
||||
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
struct mDebuggerEntryInfo debuggerInfo = {
|
||||
.address = pc,
|
||||
.type.st.traceType = isCall ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN,
|
||||
.type.st.traceType = (interrupt || isCall) ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN,
|
||||
.pointId = 0
|
||||
};
|
||||
mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
|
||||
|
|
|
@ -534,3 +534,46 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
buffer[blen - 1] = '\0';
|
||||
return total;
|
||||
}
|
||||
|
||||
uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) {
|
||||
uint32_t address = 0;
|
||||
int32_t offset = 0;
|
||||
if (info->memory.format & ARM_MEMORY_REGISTER_BASE) {
|
||||
if (info->memory.baseReg == ARM_PC && info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
|
||||
address = pc;
|
||||
} else {
|
||||
address = regs->gprs[info->memory.baseReg];
|
||||
}
|
||||
}
|
||||
if (info->memory.format & ARM_MEMORY_POST_INCREMENT) {
|
||||
return address;
|
||||
}
|
||||
if (info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
|
||||
offset = info->memory.offset.immediate;
|
||||
} else if (info->memory.format & ARM_MEMORY_REGISTER_OFFSET) {
|
||||
offset = info->memory.offset.reg == ARM_PC ? pc : regs->gprs[info->memory.offset.reg];
|
||||
}
|
||||
if (info->memory.format & ARM_MEMORY_SHIFTED_OFFSET) {
|
||||
uint8_t shiftSize = info->memory.offset.shifterImm;
|
||||
switch (info->memory.offset.shifterOp) {
|
||||
case ARM_SHIFT_LSL:
|
||||
offset <<= shiftSize;
|
||||
break;
|
||||
case ARM_SHIFT_LSR:
|
||||
offset = ((uint32_t) offset) >> shiftSize;
|
||||
break;
|
||||
case ARM_SHIFT_ASR:
|
||||
offset >>= shiftSize;
|
||||
break;
|
||||
case ARM_SHIFT_ROR:
|
||||
offset = ROR(offset, shiftSize);
|
||||
break;
|
||||
case ARM_SHIFT_RRX:
|
||||
offset = (regs->cpsr.c << 31) | ((uint32_t) offset >> 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
return address + (info->memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -offset : offset);
|
||||
}
|
||||
|
|
|
@ -152,7 +152,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si
|
|||
#ifdef FIXED_ROM_BUFFER
|
||||
extern uint32_t* romBuffer;
|
||||
extern size_t romBufferSize;
|
||||
vfm = VFileFromMemory(romBuffer, romBufferSize);
|
||||
if (size > romBufferSize) {
|
||||
size = romBufferSize;
|
||||
}
|
||||
vfm = VFileFromMemory(romBuffer, size);
|
||||
#else
|
||||
vfm = VFileMemChunk(NULL, size);
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,10 @@ void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t
|
|||
*previous = event;
|
||||
}
|
||||
|
||||
void mTimingScheduleAbsolute(struct mTiming* timing, struct mTimingEvent* event, int32_t when) {
|
||||
mTimingSchedule(timing, event, when - mTimingCurrentTime(timing));
|
||||
}
|
||||
|
||||
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
|
||||
if (timing->reroot) {
|
||||
timing->root = timing->reroot;
|
||||
|
|
|
@ -1035,7 +1035,13 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
case DEBUGGER_ENTER_STACK:
|
||||
if (info) {
|
||||
if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address);
|
||||
struct mStackTrace* stack = &cliDebugger->d.stackTrace;
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
if (frame->interrupt) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit interrupt at at 0x%08X\n", info->address);
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address);
|
||||
}
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address);
|
||||
}
|
||||
|
|
|
@ -654,7 +654,7 @@ void mGUIRunloop(struct mGUIRunner* runner) {
|
|||
mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
|
||||
}
|
||||
}
|
||||
while (runner->running(runner)) {
|
||||
while (!runner->running || runner->running(runner)) {
|
||||
char path[PATH_MAX];
|
||||
const char* preselect = mCoreConfigGetValue(&runner->config, "lastGame");
|
||||
if (preselect) {
|
||||
|
|
|
@ -117,7 +117,7 @@ void GBAudioReset(struct GBAudio* audio) {
|
|||
if (audio->style == GB_AUDIO_GBA) {
|
||||
mTimingSchedule(audio->timing, &audio->frameEvent, 0);
|
||||
}
|
||||
audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
||||
audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } };
|
||||
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
||||
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
|
||||
audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 };
|
||||
|
@ -964,6 +964,7 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL
|
|||
|
||||
void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) {
|
||||
uint32_t flags = 0;
|
||||
uint32_t sweep = 0;
|
||||
uint32_t ch1Flags = 0;
|
||||
uint32_t ch2Flags = 0;
|
||||
uint32_t ch4Flags = 0;
|
||||
|
@ -980,7 +981,9 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
|||
ch1Flags = GBSerializedAudioEnvelopeSetLength(ch1Flags, audio->ch1.control.length);
|
||||
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
|
||||
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
|
||||
sweep = GBSerializedAudioSweepSetTime(sweep, audio->ch1.sweep.time & 7);
|
||||
STORE_32LE(ch1Flags, 0, &state->ch1.envelope);
|
||||
STORE_32LE(sweep, 0, &state->ch1.sweep);
|
||||
STORE_32LE(audio->ch1Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextEvent);
|
||||
|
||||
flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
|
||||
|
@ -1011,6 +1014,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
|||
|
||||
void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGState* state, const uint32_t* flagsIn) {
|
||||
uint32_t flags;
|
||||
uint32_t sweep;
|
||||
uint32_t ch1Flags = 0;
|
||||
uint32_t ch2Flags = 0;
|
||||
uint32_t ch4Flags = 0;
|
||||
|
@ -1032,11 +1036,16 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->skipFrame = GBSerializedAudioFlagsGetSkipFrame(flags);
|
||||
|
||||
LOAD_32LE(ch1Flags, 0, &state->ch1.envelope);
|
||||
LOAD_32LE(sweep, 0, &state->ch1.sweep);
|
||||
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags);
|
||||
audio->ch1.envelope.dead = GBSerializedAudioFlagsGetCh1Dead(flags);
|
||||
audio->ch1.control.hi = GBSerializedAudioFlagsGetCh1Hi(flags);
|
||||
audio->ch1.sweep.enable = GBSerializedAudioFlagsGetCh1SweepEnabled(flags);
|
||||
audio->ch1.sweep.occurred = GBSerializedAudioFlagsGetCh1SweepOccurred(flags);
|
||||
audio->ch1.sweep.time = GBSerializedAudioSweepGetTime(sweep);
|
||||
if (!audio->ch1.sweep.time) {
|
||||
audio->ch1.sweep.time = 8;
|
||||
}
|
||||
audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
|
||||
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
|
||||
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags);
|
||||
|
|
|
@ -699,7 +699,8 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memcpy(gb->memory.io, state->io, GB_SIZE_IO);
|
||||
gb->memory.ie = state->ie;
|
||||
|
||||
if (GBAudioEnableGetEnable(*gb->audio.nr52)) {
|
||||
gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52);
|
||||
if (GBAudioEnableGetEnable(gb->audio.enable)) {
|
||||
GBIOWrite(gb, REG_NR10, gb->memory.io[REG_NR10]);
|
||||
GBIOWrite(gb, REG_NR11, gb->memory.io[REG_NR11]);
|
||||
GBIOWrite(gb, REG_NR12, gb->memory.io[REG_NR12]);
|
||||
|
@ -709,14 +710,14 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->audio.ch1.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR14] << 8);
|
||||
GBIOWrite(gb, REG_NR21, gb->memory.io[REG_NR21]);
|
||||
GBIOWrite(gb, REG_NR22, gb->memory.io[REG_NR22]);
|
||||
GBIOWrite(gb, REG_NR22, gb->memory.io[REG_NR23]);
|
||||
GBIOWrite(gb, REG_NR23, gb->memory.io[REG_NR23]);
|
||||
gb->audio.ch2.control.frequency &= 0xFF;
|
||||
gb->audio.ch2.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[REG_NR24] << 8);
|
||||
gb->audio.ch2.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR24] << 8);
|
||||
GBIOWrite(gb, REG_NR30, gb->memory.io[REG_NR30]);
|
||||
GBIOWrite(gb, REG_NR31, gb->memory.io[REG_NR31]);
|
||||
GBIOWrite(gb, REG_NR32, gb->memory.io[REG_NR32]);
|
||||
GBIOWrite(gb, REG_NR32, gb->memory.io[REG_NR33]);
|
||||
GBIOWrite(gb, REG_NR33, gb->memory.io[REG_NR33]);
|
||||
gb->audio.ch3.rate &= 0xFF;
|
||||
gb->audio.ch3.rate |= GBAudioRegisterControlGetRate(gb->memory.io[REG_NR34] << 8);
|
||||
gb->audio.ch3.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR34] << 8);
|
||||
|
|
|
@ -440,6 +440,7 @@ static void _GBMBC1Update(struct GB* gb) {
|
|||
GBMBCSwitchSramBank(gb, 0);
|
||||
}
|
||||
if (!(state->bankLo & 0x1F)) {
|
||||
++state->bankLo;
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
|
|
|
@ -211,6 +211,7 @@ void GBMemoryReset(struct GB* gb) {
|
|||
switch (gb->memory.mbcType) {
|
||||
case GB_MBC1:
|
||||
gb->memory.mbcState.mbc1.mode = 0;
|
||||
gb->memory.mbcState.mbc1.bankLo = 1;
|
||||
break;
|
||||
case GB_MBC6:
|
||||
GBMBCSwitchHalfBank(gb, 0, 2);
|
||||
|
@ -784,10 +785,14 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
LOAD_32LE(when, 0, &state->memory.dmaNext);
|
||||
if (memory->dmaRemaining) {
|
||||
mTimingSchedule(&gb->timing, &memory->dmaEvent, when);
|
||||
} else {
|
||||
memory->dmaEvent.when = when + mTimingCurrentTime(&gb->timing);
|
||||
}
|
||||
LOAD_32LE(when, 0, &state->memory.hdmaNext);
|
||||
if (memory->hdmaRemaining) {
|
||||
mTimingSchedule(&gb->timing, &memory->hdmaEvent, when);
|
||||
} else {
|
||||
memory->hdmaEvent.when = when + mTimingCurrentTime(&gb->timing);
|
||||
}
|
||||
|
||||
GBSerializedMemoryFlags flags;
|
||||
|
|
|
@ -188,6 +188,8 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
LOAD_32LE(when, 0, &state->cpu.eiPending);
|
||||
if (GBSerializedCpuFlagsIsEiPending(flags)) {
|
||||
mTimingSchedule(&gb->timing, &gb->eiPending, when);
|
||||
} else {
|
||||
gb->eiPending.when = when + mTimingCurrentTime(&gb->timing);
|
||||
}
|
||||
|
||||
gb->model = state->model;
|
||||
|
|
|
@ -140,8 +140,10 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s
|
|||
|
||||
GBSerializedTimerFlags flags = state->timer.flags;
|
||||
|
||||
LOAD_32LE(when, 0, &state->timer.nextIRQ);
|
||||
if (GBSerializedTimerFlagsIsIrqPending(flags)) {
|
||||
LOAD_32LE(when, 0, &state->timer.nextIRQ);
|
||||
mTimingSchedule(&timer->p->timing, &timer->irq, when);
|
||||
} else {
|
||||
timer->irq.when = when + mTimingCurrentTime(&timer->p->timing);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -904,13 +904,17 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
|
|||
}
|
||||
|
||||
uint32_t when;
|
||||
LOAD_32LE(when, 0, &state->video.nextMode);
|
||||
if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) {
|
||||
LOAD_32LE(when, 0, &state->video.nextMode);
|
||||
mTimingSchedule(&video->p->timing, &video->modeEvent, when);
|
||||
} else {
|
||||
video->modeEvent.when = when + mTimingCurrentTime(&video->p->timing);
|
||||
}
|
||||
LOAD_32LE(when, 0, &state->video.nextFrame);
|
||||
if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) {
|
||||
LOAD_32LE(when, 0, &state->video.nextFrame);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, when);
|
||||
} else {
|
||||
video->frameEvent.when = when + mTimingCurrentTime(&video->p->timing);
|
||||
}
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
|
|
|
@ -423,7 +423,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
|
||||
switch (immediate) {
|
||||
case 0xF0: // Used for internal stall counting
|
||||
cpu->gprs[4] = gba->biosStall;
|
||||
cpu->gprs[12] = gba->biosStall;
|
||||
return;
|
||||
case 0xFA:
|
||||
GBAPrintFlush(gba);
|
||||
|
|
|
@ -9,17 +9,17 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
|||
0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x4c, 0x01, 0x9f, 0x15,
|
||||
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03,
|
||||
0x20, 0xd0, 0x4d, 0x02, 0x10, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5,
|
||||
0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5,
|
||||
0xd4, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7, 0xd2, 0xcf, 0xa0, 0xe3,
|
||||
0x0b, 0x00, 0x5c, 0xe1, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xc0, 0x4f, 0xe1,
|
||||
0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3,
|
||||
0x0c, 0xf0, 0x21, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0x5b, 0xe3,
|
||||
0x0b, 0x00, 0x5c, 0xe1, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9,
|
||||
0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, 0x0c, 0xf0, 0x21, 0xe1,
|
||||
0x00, 0x00, 0xf0, 0x0f, 0x04, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0x5b, 0xe3,
|
||||
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x40, 0xbd, 0xe8,
|
||||
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8,
|
||||
0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1,
|
||||
0x10, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
||||
0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
||||
0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00,
|
||||
|
@ -73,5 +73,5 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
|||
0x01, 0xa0, 0xa0, 0xe1, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8,
|
||||
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
|
||||
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00,
|
||||
0x04, 0x40, 0x54, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1
|
||||
0x04, 0xc0, 0x5c, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1
|
||||
};
|
||||
|
|
|
@ -31,19 +31,19 @@ swiBase:
|
|||
cmp sp, #0
|
||||
moveq sp, #0x04000000
|
||||
subeq sp, #0x20
|
||||
stmfd sp!, {r4, r11-r12, lr}
|
||||
stmfd sp!, {r11-r12, lr}
|
||||
ldrb r11, [lr, #-2]
|
||||
mov r12, #swiTable
|
||||
ldr r11, [r12, r11, lsl #2]
|
||||
mov r12, #StallCall
|
||||
cmp r12, r11
|
||||
swieq 0xF00000 @ Special mGBA-internal call to load the stall count into r4
|
||||
mrs r12, spsr
|
||||
stmfd sp!, {r12}
|
||||
and r12, #0x80
|
||||
orr r12, #0x1F
|
||||
msr cpsr_c, r12
|
||||
stmfd sp!, {lr}
|
||||
swieq 0xF00000 @ Special mGBA-internal call to load the stall count into r12
|
||||
stmfd sp!, {r2, lr}
|
||||
cmp r11, #0
|
||||
nop
|
||||
nop
|
||||
|
@ -56,11 +56,11 @@ bxne r11
|
|||
nop
|
||||
nop
|
||||
nop
|
||||
ldmfd sp!, {lr}
|
||||
ldmfd sp!, {r2, lr}
|
||||
msr cpsr, #0x93
|
||||
ldmfd sp!, {r12}
|
||||
msr spsr, r12
|
||||
ldmfd sp!, {r4, r11-r12, lr}
|
||||
ldmfd sp!, {r11-r12, lr}
|
||||
movs pc, lr
|
||||
.word 0
|
||||
.word 0xE3A02004
|
||||
|
@ -310,6 +310,6 @@ ArcTan:
|
|||
ArcTan2:
|
||||
|
||||
StallCall:
|
||||
subs r4, #4
|
||||
subs r12, #4
|
||||
bhi StallCall
|
||||
bx lr
|
||||
|
|
|
@ -293,7 +293,7 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = {
|
|||
// Audio
|
||||
1, 1, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
// DMA
|
||||
|
@ -980,6 +980,8 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
|
||||
}
|
||||
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
|
||||
gba->sio.siocnt = gba->memory.io[REG_SIOCNT >> 1];
|
||||
GBASIOWriteRCNT(&gba->sio, gba->memory.io[REG_RCNT >> 1]);
|
||||
|
||||
LOAD_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister);
|
||||
LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC);
|
||||
|
|
|
@ -69,7 +69,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
}
|
||||
miscFlags = GBASerializedMiscFlagsSetBlocked(miscFlags, gba->cpuBlocked);
|
||||
STORE_32(miscFlags, 0, &state->miscFlags);
|
||||
STORE_32(gba->biosStall, 0, &state->nextIrq);
|
||||
STORE_32(gba->biosStall, 0, &state->biosStall);
|
||||
|
||||
GBAMemorySerialize(&gba->memory, state);
|
||||
GBAIOSerialize(gba, state);
|
||||
|
@ -188,7 +188,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
|
||||
}
|
||||
gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags);
|
||||
LOAD_32(gba->biosStall, 0, &state->nextIrq);
|
||||
LOAD_32(gba->biosStall, 0, &state->biosStall);
|
||||
|
||||
GBAVideoDeserialize(&gba->video, state);
|
||||
GBAMemoryDeserialize(&gba->memory, state);
|
||||
|
|
|
@ -69,7 +69,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec
|
|||
return false;
|
||||
}
|
||||
LOAD_32(size, 0, &buffer.i);
|
||||
if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
|
||||
if (size < 0x1C || size >= SIZE_CART_FLASH1M + 0x1C) {
|
||||
return false;
|
||||
}
|
||||
char* payload = malloc(size);
|
||||
|
@ -91,7 +91,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec
|
|||
buffer.c[0x19] = 0;
|
||||
buffer.c[0x1A] = 0;
|
||||
buffer.c[0x1B] = 0;
|
||||
if (memcmp(buffer.c, payload, 0x1C) != 0) {
|
||||
if (memcmp(buffer.c, payload, testChecksum ? 0x1C : 0xF) != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,9 +134,8 @@ void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timi
|
|||
tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
|
||||
currentTime += tickIncrement;
|
||||
currentTime &= ~tickMask;
|
||||
currentTime -= mTimingCurrentTime(timing);
|
||||
mTimingDeschedule(timing, &timer->event);
|
||||
mTimingSchedule(timing, &timer->event, currentTime);
|
||||
mTimingScheduleAbsolute(timing, &timer->event, currentTime);
|
||||
}
|
||||
|
||||
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
|
||||
|
|
|
@ -154,14 +154,13 @@ AccessControlInfo:
|
|||
- cam:u
|
||||
- cecd:u
|
||||
- cfg:u
|
||||
- dlp:FKCL
|
||||
- dlp:SRVR
|
||||
- dsp::DSP
|
||||
- frd:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- hid:USER
|
||||
- http:C
|
||||
- mcu::HWC
|
||||
- mic:u
|
||||
- ndm:u
|
||||
- news:u
|
||||
|
|
|
@ -187,6 +187,7 @@ static void _cleanup(void) {
|
|||
camExit();
|
||||
ndspExit();
|
||||
ptmuExit();
|
||||
mcuHwcExit();
|
||||
}
|
||||
|
||||
static void _map3DSKey(struct mInputMap* map, int ctrKey, enum GBAKey key) {
|
||||
|
@ -237,15 +238,20 @@ static void _drawEnd(void) {
|
|||
static int _batteryState(void) {
|
||||
u8 charge;
|
||||
u8 adapter;
|
||||
PTMU_GetBatteryLevel(&charge);
|
||||
PTMU_GetBatteryChargeState(&adapter);
|
||||
|
||||
int state = 0;
|
||||
if (R_SUCCEEDED(MCUHWC_GetBatteryLevel(&charge))) {
|
||||
charge |= BATTERY_PERCENTAGE_VALID;
|
||||
} else {
|
||||
PTMU_GetBatteryLevel(&charge);
|
||||
if (charge > 0) {
|
||||
--charge;
|
||||
}
|
||||
}
|
||||
if (adapter) {
|
||||
state |= BATTERY_CHARGING;
|
||||
}
|
||||
if (charge > 0) {
|
||||
--charge;
|
||||
}
|
||||
return state | charge;
|
||||
}
|
||||
|
||||
|
@ -815,6 +821,7 @@ int main() {
|
|||
camera.cam = SELECT_IN1;
|
||||
|
||||
ptmuInit();
|
||||
mcuHwcInit();
|
||||
camInit();
|
||||
|
||||
hasSound = NO_SOUND;
|
||||
|
|
|
@ -72,13 +72,12 @@ static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) {
|
|||
}
|
||||
|
||||
static int _batteryState(void) {
|
||||
int charge = scePowerGetBatteryLifePercent();
|
||||
int charge = scePowerGetBatteryLifePercent() | BATTERY_PERCENTAGE_VALID;
|
||||
int adapter = scePowerIsPowerOnline();
|
||||
int state = 0;
|
||||
if (adapter) {
|
||||
state |= BATTERY_CHARGING;
|
||||
}
|
||||
charge /= 25;
|
||||
return state | charge;
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ void FrameView::disableLayer(const QPointF& coord) {
|
|||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
void FrameView::updateTilesGBA(bool force) {
|
||||
void FrameView::updateTilesGBA(bool) {
|
||||
if (m_ui.freeze->checkState() == Qt::Checked) {
|
||||
return;
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ void FrameView::injectGBA() {
|
|||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
void FrameView::updateTilesGB(bool force) {
|
||||
void FrameView::updateTilesGB(bool) {
|
||||
if (m_ui.freeze->checkState() == Qt::Checked) {
|
||||
return;
|
||||
}
|
||||
|
@ -314,10 +314,12 @@ void FrameView::invalidateQueue(const QSize& dims) {
|
|||
#ifdef M_CORE_GBA
|
||||
case PLATFORM_GBA:
|
||||
injectGBA();
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case PLATFORM_GB:
|
||||
injectGB();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
m_vl->runFrame(m_vl);
|
||||
|
@ -376,6 +378,8 @@ bool FrameView::eventFilter(QObject* obj, QEvent* event) {
|
|||
pos /= m_ui.magnification->value();
|
||||
disableLayer(pos);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -462,4 +466,4 @@ QString FrameView::LayerId::readable() const {
|
|||
return typeStr;
|
||||
}
|
||||
return tr("%1 %2").arg(typeStr).arg(index);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ bool MapView::eventFilter(QObject* obj, QEvent* event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void MapView::updateTilesGBA(bool force) {
|
||||
void MapView::updateTilesGBA(bool) {
|
||||
{
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
int bitmap = -1;
|
||||
|
|
|
@ -50,7 +50,7 @@ void Swatch::setColor(int index, uint32_t color) {
|
|||
updateFill(index);
|
||||
}
|
||||
|
||||
void Swatch::paintEvent(QPaintEvent* event) {
|
||||
void Swatch::paintEvent(QPaintEvent*) {
|
||||
QPainter painter(this);
|
||||
painter.drawPixmap(QPoint(), m_backing);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ TilePainter::TilePainter(QWidget* parent)
|
|||
setTileCount(3072);
|
||||
}
|
||||
|
||||
void TilePainter::paintEvent(QPaintEvent* event) {
|
||||
void TilePainter::paintEvent(QPaintEvent*) {
|
||||
QPainter painter(this);
|
||||
painter.drawPixmap(QPoint(), m_backing);
|
||||
}
|
||||
|
||||
void TilePainter::resizeEvent(QResizeEvent* event) {
|
||||
void TilePainter::resizeEvent(QResizeEvent*) {
|
||||
int w = width() / m_size;
|
||||
if (!w) {
|
||||
w = 1;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -607,7 +607,7 @@ static int _batteryState(void) {
|
|||
u32 charge;
|
||||
int state = 0;
|
||||
if (R_SUCCEEDED(psmGetBatteryChargePercentage(&charge))) {
|
||||
state = (charge + 12) / 25;
|
||||
state = charge | BATTERY_PERCENTAGE_VALID;
|
||||
} else {
|
||||
return BATTERY_NOT_PRESENT;
|
||||
}
|
||||
|
|
|
@ -226,6 +226,7 @@ void CIflush(struct StringBuilder* builder, FILE* out) {
|
|||
builder->repeat = 0;
|
||||
fputs(string, out);
|
||||
free(string);
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
static bool parseCInemaArgs(int argc, char* const* argv) {
|
||||
|
@ -736,7 +737,7 @@ static void _writeDiff(const char* testName, const struct CInemaImage* image, si
|
|||
return;
|
||||
}
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "%s_%04" PRIz "u.png", type, frame);
|
||||
snprintf(name, sizeof(name), "%s_%05" PRIz "u.png", type, frame);
|
||||
struct VFile* vf = dir->openFile(dir, name, O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (!vf) {
|
||||
CIerr(0, "Could not open output file %s\n", name);
|
||||
|
@ -792,12 +793,21 @@ static bool _compareImages(struct CInemaTest* restrict test, const struct CInema
|
|||
for (x = 0; x < image->width; ++x) {
|
||||
size_t pix = expected->stride * y + x;
|
||||
size_t tpix = image->stride * y + x;
|
||||
#ifndef __BIG_ENDIAN__
|
||||
int testR = testPixels[tpix * 4 + 0];
|
||||
int testG = testPixels[tpix * 4 + 1];
|
||||
int testB = testPixels[tpix * 4 + 2];
|
||||
int expectR = expectPixels[pix * 4 + 0];
|
||||
int expectG = expectPixels[pix * 4 + 1];
|
||||
int expectB = expectPixels[pix * 4 + 2];
|
||||
#else
|
||||
int testB = testPixels[tpix * 4 + 1];
|
||||
int testG = testPixels[tpix * 4 + 2];
|
||||
int testR = testPixels[tpix * 4 + 3];
|
||||
int expectB = expectPixels[pix * 4 + 1];
|
||||
int expectG = expectPixels[pix * 4 + 2];
|
||||
int expectR = expectPixels[pix * 4 + 3];
|
||||
#endif
|
||||
int r = expectR - testR;
|
||||
int g = expectG - testG;
|
||||
int b = expectB - testB;
|
||||
|
@ -828,9 +838,15 @@ static bool _compareImages(struct CInemaTest* restrict test, const struct CInema
|
|||
if (b > *max) {
|
||||
*max = b;
|
||||
}
|
||||
#ifndef __BIG_ENDIAN__
|
||||
diff[pix * 4 + 0] = r;
|
||||
diff[pix * 4 + 1] = g;
|
||||
diff[pix * 4 + 2] = b;
|
||||
#else
|
||||
diff[pix * 4 + 1] = b;
|
||||
diff[pix * 4 + 2] = g;
|
||||
diff[pix * 4 + 3] = r;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (test) {
|
||||
|
@ -864,13 +880,21 @@ void _writeDiffSet(struct CInemaImage* expected, const char* name, uint8_t* diff
|
|||
for (y = 0; y < outdiff.height; ++y) {
|
||||
for (x = 0; x < outdiff.width; ++x) {
|
||||
size_t pix = outdiff.stride * y + x;
|
||||
#ifndef __BIG_ENDIAN__
|
||||
diff[pix * 4 + 0] = diff[pix * 4 + 0] * 255 / max;
|
||||
diff[pix * 4 + 1] = diff[pix * 4 + 1] * 255 / max;
|
||||
diff[pix * 4 + 2] = diff[pix * 4 + 2] * 255 / max;
|
||||
#else
|
||||
diff[pix * 4 + 1] = diff[pix * 4 + 1] * 255 / max;
|
||||
diff[pix * 4 + 2] = diff[pix * 4 + 2] * 255 / max;
|
||||
diff[pix * 4 + 3] = diff[pix * 4 + 3] * 255 / max;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (xfail) {
|
||||
_writeDiff(name, &outdiff, frame, "xnormalized");
|
||||
} else {
|
||||
_writeDiff(name, &outdiff, frame, "normalized");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,7 +999,7 @@ void CInemaTestRun(struct CInemaTest* test) {
|
|||
core->setVideoBuffer(core, image.data, image.stride);
|
||||
mCoreConfigInit(&core->config, "cinema");
|
||||
|
||||
unsigned limit = 9999;
|
||||
unsigned limit = 3600;
|
||||
unsigned skip = 0;
|
||||
unsigned fail = 0;
|
||||
unsigned video = 0;
|
||||
|
@ -1086,6 +1110,14 @@ void CInemaTestRun(struct CInemaTest* test) {
|
|||
if (!FFmpegDecoderRead(&decoder)) {
|
||||
CIlog(1, "Failed to read more frames. EOF?\n");
|
||||
test->status = CI_FAIL;
|
||||
if (rebaseline && !FFmpegEncoderIsOpen(&encoder)) {
|
||||
_replayBaseline(test, &encoder, &image, frame);
|
||||
if (test->status == CI_ERROR) {
|
||||
break;
|
||||
}
|
||||
encoder.d.postVideoFrame(&encoder.d, image.data, image.stride);
|
||||
core->setAVStream(core, &encoder.d);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,11 +137,11 @@ const struct GUIFontGlyphMetric defaultFontMetrics[128] = {
|
|||
};
|
||||
|
||||
const struct GUIIconMetric defaultIconMetrics[] = {
|
||||
[GUI_ICON_BATTERY_FULL] = { 0, 0, 32, 16 },
|
||||
[GUI_ICON_BATTERY_HIGH] = { 32, 0, 32, 16 },
|
||||
[GUI_ICON_BATTERY_HALF] = { 64, 0, 32, 16 },
|
||||
[GUI_ICON_BATTERY_LOW] = { 96, 0, 32, 16 },
|
||||
[GUI_ICON_BATTERY_EMPTY] = { 128, 0, 32, 16 },
|
||||
[GUI_ICON_BATTERY_FULL] = { 0, 2, 32, 12 },
|
||||
[GUI_ICON_BATTERY_HIGH] = { 32, 2, 32, 12 },
|
||||
[GUI_ICON_BATTERY_HALF] = { 64, 2, 32, 12 },
|
||||
[GUI_ICON_BATTERY_LOW] = { 96, 2, 32, 12 },
|
||||
[GUI_ICON_BATTERY_EMPTY] = { 128, 2, 32, 12 },
|
||||
[GUI_ICON_SCROLLBAR_BUTTON] = { 6, 16, 4, 5 },
|
||||
[GUI_ICON_SCROLLBAR_TRACK] = { 23, 16, 2, 16 },
|
||||
[GUI_ICON_SCROLLBAR_THUMB] = { 38, 16, 4, 16 },
|
||||
|
|
|
@ -43,7 +43,7 @@ void GUIFontPrint(const struct GUIFont* font, int x, int y, enum GUIAlignment al
|
|||
if (c == '\1') {
|
||||
c = utf8Char(&text, &len);
|
||||
if (c < GUI_ICON_MAX) {
|
||||
GUIFontDrawIcon(font, x, y, GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, c);
|
||||
GUIFontDrawIcon(font, x, y, (align & GUI_ALIGN_HCENTER) | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, c);
|
||||
unsigned w;
|
||||
GUIFontIconMetrics(font, c, &w, 0);
|
||||
x += w;
|
||||
|
|
|
@ -264,20 +264,20 @@ void GUIDrawBattery(struct GUIParams* params) {
|
|||
return;
|
||||
}
|
||||
uint32_t color = 0xFF000000;
|
||||
if (state == (BATTERY_CHARGING | BATTERY_FULL)) {
|
||||
if ((state & (BATTERY_CHARGING | BATTERY_FULL)) == (BATTERY_CHARGING | BATTERY_FULL)) {
|
||||
color |= 0xFFC060;
|
||||
} else if (state & BATTERY_CHARGING) {
|
||||
color |= 0x60FF60;
|
||||
} else if (state >= BATTERY_HALF) {
|
||||
} else if ((state & BATTERY_VALUE) >= BATTERY_HALF) {
|
||||
color |= 0xFFFFFF;
|
||||
} else if (state == BATTERY_LOW) {
|
||||
} else if ((state & BATTERY_VALUE) >= BATTERY_LOW) {
|
||||
color |= 0x30FFFF;
|
||||
} else {
|
||||
color |= 0x3030FF;
|
||||
}
|
||||
|
||||
enum GUIIcon batteryIcon;
|
||||
switch (state & ~BATTERY_CHARGING) {
|
||||
switch ((state & BATTERY_VALUE) - (state & BATTERY_VALUE) % 25) {
|
||||
case BATTERY_EMPTY:
|
||||
batteryIcon = GUI_ICON_BATTERY_EMPTY;
|
||||
break;
|
||||
|
@ -298,7 +298,12 @@ void GUIDrawBattery(struct GUIParams* params) {
|
|||
break;
|
||||
}
|
||||
|
||||
GUIFontDrawIcon(params->font, params->width, 0, GUI_ALIGN_RIGHT, GUI_ORIENT_0, color, batteryIcon);
|
||||
GUIFontDrawIcon(params->font, params->width, GUIFontHeight(params->font) + 2, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, batteryIcon);
|
||||
if (state & BATTERY_PERCENTAGE_VALID) {
|
||||
unsigned width;
|
||||
GUIFontIconMetrics(params->font, batteryIcon, &width, NULL);
|
||||
GUIFontPrintf(params->font, params->width - width, GUIFontHeight(params->font), GUI_ALIGN_RIGHT, color, "%u%%", state & BATTERY_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
void GUIDrawClock(struct GUIParams* params) {
|
||||
|
|
|
@ -137,6 +137,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
|
|||
|
||||
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
msync(memory, size, MS_SYNC);
|
||||
munmap(memory, size);
|
||||
}
|
||||
#else
|
||||
|
@ -167,6 +168,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
|
|||
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
|
||||
UNUSED(size);
|
||||
struct VFileFD* vfd = (struct VFileFD*) vf;
|
||||
FlushViewOfFile(memory, size);
|
||||
size_t i;
|
||||
for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) {
|
||||
if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) {
|
||||
|
@ -214,6 +216,9 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
|
|||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st, &ft);
|
||||
SetFileTime(h, NULL, &ft, &ft);
|
||||
if (buffer && size) {
|
||||
FlushViewOfFile(buffer, size);
|
||||
}
|
||||
return FlushFileBuffers(h);
|
||||
#endif
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue