Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-11 02:03:19 -07:00
commit 9ff5ef9d04
43 changed files with 596 additions and 407 deletions

View File

@ -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:

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -440,6 +440,7 @@ static void _GBMBC1Update(struct GB* gb) {
GBMBCSwitchSramBank(gb, 0);
}
if (!(state->bankLo & 0x1F)) {
++state->bankLo;
++bank;
}
GBMBCSwitchBank(gb, bank);

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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
};

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 },

View File

@ -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;

View File

@ -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) {

View File

@ -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
}