diff --git a/src/gba/audio.c b/src/gba/audio.c index 1b2d121e9..965e03c44 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -848,75 +848,84 @@ static void _sample(struct GBAAudio* audio) { } void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { - state->audio.ch1Volume = audio->ch1.envelope.currentVolume; - state->audio.ch1Dead = audio->ch1.envelope.dead; - state->audio.ch1Hi = audio->ch1.control.hi; - state->audio.ch1.envelopeNextStep = audio->ch1.envelope.nextStep; - state->audio.ch1.waveNextStep = audio->ch1.control.nextStep; - state->audio.ch1.sweepNextStep = audio->ch1.nextSweep; - state->audio.ch1.endTime = audio->ch1.control.endTime; - state->audio.ch1.nextEvent = audio->nextCh1; + uint32_t flags = 0; - state->audio.ch2Volume = audio->ch2.envelope.currentVolume; - state->audio.ch2Dead = audio->ch2.envelope.dead; - state->audio.ch2Hi = audio->ch2.control.hi; - state->audio.ch2.envelopeNextStep = audio->ch2.envelope.nextStep; - state->audio.ch2.waveNextStep = audio->ch2.control.nextStep; - state->audio.ch2.endTime = audio->ch2.control.endTime; - state->audio.ch2.nextEvent = audio->nextCh2; + flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume); + flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead); + flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->ch1.control.hi); + STORE_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); + STORE_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); + STORE_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); + STORE_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime); + STORE_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent); + + flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume); + flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead); + flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi); + STORE_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); + STORE_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); + STORE_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime); + STORE_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent); memcpy(state->audio.ch3.wavebanks, audio->ch3.wavedata, sizeof(state->audio.ch3.wavebanks)); - state->audio.ch3.endTime = audio->ch3.control.endTime; - state->audio.ch3.nextEvent = audio->nextCh3; + STORE_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime); + STORE_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent); - state->audio.ch4Volume = audio->ch4.envelope.currentVolume; - state->audio.ch4Dead = audio->ch4.envelope.dead; - state->audio.ch4.envelopeNextStep = audio->ch4.envelope.nextStep; - state->audio.ch4.lfsr = audio->ch4.lfsr; - state->audio.ch4.endTime = audio->ch4.control.endTime; - state->audio.ch4.nextEvent = audio->nextCh4; + state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume); + state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead); + STORE_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); + STORE_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr); + STORE_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime); + STORE_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent); + + STORE_32(flags, 0, &state->audio.flags); CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA)); CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB)); - state->audio.fifoSize = CircleBufferSize(&audio->chA.fifo); + uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo); + STORE_32(fifoSize, 0, &state->audio.fifoSize); - state->audio.nextEvent = audio->nextEvent; - state->audio.eventDiff = audio->eventDiff; - state->audio.nextSample = audio->nextSample; + STORE_32(audio->nextEvent, 0, &state->audio.nextEvent); + STORE_32(audio->eventDiff, 0, &state->audio.eventDiff); + STORE_32(audio->nextSample, 0, &state->audio.nextSample); } void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) { - audio->ch1.envelope.currentVolume = state->audio.ch1Volume; - audio->ch1.envelope.dead = state->audio.ch1Dead; - audio->ch1.control.hi = state->audio.ch1Hi; - audio->ch1.envelope.nextStep = state->audio.ch1.envelopeNextStep; - audio->ch1.control.nextStep = state->audio.ch1.waveNextStep; - audio->ch1.nextSweep = state->audio.ch1.sweepNextStep; - audio->ch1.control.endTime = state->audio.ch1.endTime; - audio->nextCh1 = state->audio.ch1.nextEvent; + uint32_t flags; + LOAD_32(flags, 0, &state->audio.flags); + audio->ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags); + audio->ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags); + audio->ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags); + LOAD_32(audio->ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); + LOAD_32(audio->ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); + LOAD_32(audio->ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); + LOAD_32(audio->ch1.control.endTime, 0, &state->audio.ch1.endTime); + LOAD_32(audio->nextCh1, 0, &state->audio.ch1.nextEvent); - audio->ch2.envelope.currentVolume = state->audio.ch2Volume; - audio->ch2.envelope.dead = state->audio.ch2Dead; - audio->ch2.control.hi = state->audio.ch2Hi; - audio->ch2.envelope.nextStep = state->audio.ch2.envelopeNextStep; - audio->ch2.control.nextStep = state->audio.ch2.waveNextStep; - audio->ch2.control.endTime = state->audio.ch2.endTime; - audio->nextCh2 = state->audio.ch2.nextEvent; + audio->ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags); + audio->ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags); + audio->ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags); + LOAD_32(audio->ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); + LOAD_32(audio->ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); + LOAD_32(audio->ch2.control.endTime, 0, &state->audio.ch2.endTime); + LOAD_32(audio->nextCh2, 0, &state->audio.ch2.nextEvent); + // TODO: Big endian? memcpy(audio->ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->ch3.wavedata)); - audio->ch3.control.endTime = state->audio.ch3.endTime; - audio->nextCh3 = state->audio.ch3.nextEvent; + LOAD_32(audio->ch3.control.endTime, 0, &state->audio.ch3.endTime); + LOAD_32(audio->nextCh3, 0, &state->audio.ch3.nextEvent); - audio->ch4.envelope.currentVolume = state->audio.ch4Volume; - audio->ch4.envelope.dead = state->audio.ch4Dead; - audio->ch4.envelope.nextStep = state->audio.ch4.envelopeNextStep; - audio->ch4.lfsr = state->audio.ch4.lfsr; - audio->ch4.control.endTime = state->audio.ch4.endTime; - audio->nextCh4 = state->audio.ch4.nextEvent; + audio->ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags); + audio->ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags); + LOAD_32(audio->ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); + LOAD_32(audio->ch4.lfsr, 0, &state->audio.ch4.lfsr); + LOAD_32(audio->ch4.control.endTime, 0, &state->audio.ch4.endTime); + LOAD_32(audio->nextCh4, 0, &state->audio.ch4.nextEvent); CircleBufferClear(&audio->chA.fifo); CircleBufferClear(&audio->chB.fifo); - size_t fifoSize = state->audio.fifoSize; + uint32_t fifoSize; + LOAD_32(fifoSize, 0, &state->audio.fifoSize); if (state->audio.fifoSize > CircleBufferCapacity(&audio->chA.fifo)) { fifoSize = CircleBufferCapacity(&audio->chA.fifo); } @@ -926,9 +935,9 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]); } - audio->nextEvent = state->audio.nextEvent; - audio->eventDiff = state->audio.eventDiff; - audio->nextSample = state->audio.nextSample; + LOAD_32(audio->nextEvent, 0, &state->audio.nextEvent); + LOAD_32(audio->eventDiff, 0, &state->audio.eventDiff); + LOAD_32(audio->nextSample, 0, &state->audio.nextSample); } float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRate) { diff --git a/src/gba/audio.h b/src/gba/audio.h index e236e01a3..31550b36a 100644 --- a/src/gba/audio.h +++ b/src/gba/audio.h @@ -128,7 +128,7 @@ struct GBAAudioChannel4 { int32_t endTime; } control; - unsigned lfsr; + uint32_t lfsr; int8_t sample; }; diff --git a/src/gba/gba.c b/src/gba/gba.c index 0b39e3f1a..ecb86f27a 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -252,7 +252,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { struct GBATimer* nextTimer; timer = &gba->timers[0]; - if (timer->enable) { + if (GBATimerFlagsIsEnable(timer->flags)) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; while (timer->nextEvent <= 0) { @@ -261,7 +261,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload; - if (timer->doIrq) { + if (GBATimerFlagsIsDoIrq(timer->flags)) { GBARaiseIRQ(gba, IRQ_TIMER0); } @@ -276,7 +276,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { } nextTimer = &gba->timers[1]; - if (nextTimer->countUp) { + if (GBATimerFlagsIsCountUp(nextTimer->flags)) { ++gba->memory.io[REG_TM1CNT_LO >> 1]; if (!gba->memory.io[REG_TM1CNT_LO >> 1]) { nextTimer->nextEvent = 0; @@ -287,7 +287,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { } timer = &gba->timers[1]; - if (timer->enable) { + if (GBATimerFlagsIsEnable(timer->flags)) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; if (timer->nextEvent <= 0) { @@ -296,7 +296,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload; - if (timer->doIrq) { + if (GBATimerFlagsIsDoIrq(timer->flags)) { GBARaiseIRQ(gba, IRQ_TIMER1); } @@ -310,12 +310,12 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { } } - if (timer->countUp) { + if (GBATimerFlagsIsCountUp(timer->flags)) { timer->nextEvent = INT_MAX; } nextTimer = &gba->timers[2]; - if (nextTimer->countUp) { + if (GBATimerFlagsIsCountUp(nextTimer->flags)) { ++gba->memory.io[REG_TM2CNT_LO >> 1]; if (!gba->memory.io[REG_TM2CNT_LO >> 1]) { nextTimer->nextEvent = 0; @@ -328,7 +328,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { } timer = &gba->timers[2]; - if (timer->enable) { + if (GBATimerFlagsIsEnable(timer->flags)) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; if (timer->nextEvent <= 0) { @@ -337,16 +337,16 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload; - if (timer->doIrq) { + if (GBATimerFlagsIsDoIrq(timer->flags)) { GBARaiseIRQ(gba, IRQ_TIMER2); } - if (timer->countUp) { + if (GBATimerFlagsIsCountUp(timer->flags)) { timer->nextEvent = INT_MAX; } nextTimer = &gba->timers[3]; - if (nextTimer->countUp) { + if (GBATimerFlagsIsCountUp(nextTimer->flags)) { ++gba->memory.io[REG_TM3CNT_LO >> 1]; if (!gba->memory.io[REG_TM3CNT_LO >> 1]) { nextTimer->nextEvent = 0; @@ -359,7 +359,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { } timer = &gba->timers[3]; - if (timer->enable) { + if (GBATimerFlagsIsEnable(timer->flags)) { timer->nextEvent -= cycles; timer->lastEvent -= cycles; if (timer->nextEvent <= 0) { @@ -368,11 +368,11 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) { gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload; - if (timer->doIrq) { + if (GBATimerFlagsIsDoIrq(timer->flags)) { GBARaiseIRQ(gba, IRQ_TIMER3); } - if (timer->countUp) { + if (GBATimerFlagsIsCountUp(timer->flags)) { timer->nextEvent = INT_MAX; } } @@ -481,47 +481,47 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { void GBATimerUpdateRegister(struct GBA* gba, int timer) { struct GBATimer* currentTimer = &gba->timers[timer]; - if (currentTimer->enable && !currentTimer->countUp) { + if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) { int32_t prefetchSkew = 0; if (gba->memory.lastPrefetchedPc - gba->memory.lastPrefetchedLoads * WORD_SIZE_THUMB >= (uint32_t) gba->cpu->gprs[ARM_PC]) { prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB; } // Reading this takes two cycles (1N+1I), so let's remove them preemptively - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent - 2 + prefetchSkew) >> currentTimer->prescaleBits); + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); } } void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { gba->timers[timer].reload = reload; - gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << gba->timers[timer].prescaleBits; + gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags); } void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { struct GBATimer* currentTimer = &gba->timers[timer]; GBATimerUpdateRegister(gba, timer); - int oldPrescale = currentTimer->prescaleBits; + unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags); switch (control & 0x0003) { case 0x0000: - currentTimer->prescaleBits = 0; + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0); break; case 0x0001: - currentTimer->prescaleBits = 6; + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6); break; case 0x0002: - currentTimer->prescaleBits = 8; + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8); break; case 0x0003: - currentTimer->prescaleBits = 10; + currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10); break; } - currentTimer->countUp = !!(control & 0x0004); - currentTimer->doIrq = !!(control & 0x0040); - currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << currentTimer->prescaleBits; - int wasEnabled = currentTimer->enable; - currentTimer->enable = !!(control & 0x0080); - if (!wasEnabled && currentTimer->enable) { - if (!currentTimer->countUp) { + currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, control & 0x0004); + currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040); + currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags); + bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags); + currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080); + if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) { + if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { currentTimer->nextEvent = gba->cpu->cycles + currentTimer->overflowInterval; } else { currentTimer->nextEvent = INT_MAX; @@ -530,12 +530,12 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { currentTimer->oldReload = currentTimer->reload; currentTimer->lastEvent = gba->cpu->cycles; gba->timersEnabled |= 1 << timer; - } else if (wasEnabled && !currentTimer->enable) { - if (!currentTimer->countUp) { + } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { + if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); } gba->timersEnabled &= ~(1 << timer); - } else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) { + } else if (GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) { // FIXME: this might be before present currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval; } diff --git a/src/gba/gba.h b/src/gba/gba.h index f4f89e5dd..d3281e22b 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -59,16 +59,19 @@ struct GBAThread; struct Patch; struct VFile; +DECL_BITFIELD(GBATimerFlags, uint32_t); +DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4); +DECL_BIT(GBATimerFlags, CountUp, 4); +DECL_BIT(GBATimerFlags, DoIrq, 5); +DECL_BIT(GBATimerFlags, Enable, 6); + struct GBATimer { uint16_t reload; uint16_t oldReload; int32_t lastEvent; int32_t nextEvent; int32_t overflowInterval; - unsigned prescaleBits : 4; - unsigned countUp : 1; - unsigned doIrq : 1; - unsigned enable : 1; + GBATimerFlags flags; }; struct GBA { diff --git a/src/gba/gui/gui-runner.c b/src/gba/gui/gui-runner.c index d169a5c79..11b0ef761 100644 --- a/src/gba/gui/gui-runner.c +++ b/src/gba/gui/gui-runner.c @@ -156,8 +156,6 @@ void GBAGUIRunloop(struct GBAGUIRunner* runner) { GUIMenuItemListInit(&stateSaveMenu.items, 9); GUIMenuItemListInit(&stateLoadMenu.items, 9); *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = (void*) RUNNER_CONTINUE }; -#if !(defined(__POWERPC__) || defined(__PPC__)) - // PPC doesn't have working savestates yet *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu }; @@ -180,7 +178,7 @@ void GBAGUIRunloop(struct GBAGUIRunner* runner) { *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_7) }; *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_8) }; *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_9) }; -#endif + *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = (void*) RUNNER_EXIT }; diff --git a/src/gba/hardware.c b/src/gba/hardware.c index af0a6a283..07463920f 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -591,41 +591,65 @@ int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles) { // == Serialization void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) { - state->hw.readWrite = hw->readWrite; - state->hw.pinState = hw->pinState; - state->hw.pinDirection = hw->direction; + GBASerializedHWFlags1 flags1 = 0; + GBASerializedHWFlags2 flags2 = 0; + flags1 = GBASerializedHWFlags1SetReadWrite(flags1, hw->readWrite); + STORE_16(hw->pinState, 0, &state->hw.pinState); + STORE_16(hw->direction, 0, &state->hw.pinDirection); state->hw.devices = hw->devices; - state->hw.rtc = hw->rtc; - state->hw.gyroSample = hw->gyroSample; - state->hw.gyroEdge = hw->gyroEdge; - state->hw.tiltSampleX = hw->tiltX; - state->hw.tiltSampleY = hw->tiltY; - state->hw.tiltState = hw->tiltState; - state->hw.lightCounter = hw->lightCounter; + + STORE_32(hw->rtc.bytesRemaining, 0, &state->hw.rtc.bytesRemaining); + STORE_32(hw->rtc.transferStep, 0, &state->hw.rtc.transferStep); + STORE_32(hw->rtc.bitsRead, 0, &state->hw.rtc.bitsRead); + STORE_32(hw->rtc.bits, 0, &state->hw.rtc.bits); + STORE_32(hw->rtc.commandActive, 0, &state->hw.rtc.commandActive); + STORE_32(hw->rtc.command, 0, &state->hw.rtc.command); + STORE_32(hw->rtc.control, 0, &state->hw.rtc.control); + memcpy(state->hw.rtc.time, hw->rtc.time, sizeof(state->hw.rtc.time)); + + STORE_16(hw->gyroSample, 0, &state->hw.gyroSample); + flags1 = GBASerializedHWFlags1SetGyroEdge(flags1, hw->gyroEdge); + STORE_16(hw->tiltX, 0, &state->hw.tiltSampleX); + STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY); + flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState); + flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter); state->hw.lightSample = hw->lightSample; - state->hw.lightEdge = hw->lightEdge; - state->hw.gbpInputsPosted = hw->gbpInputsPosted; - state->hw.gbpTxPosition = hw->gbpTxPosition; - state->hw.gbpNextEvent = hw->gbpNextEvent; + flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge); + flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->gbpInputsPosted); + flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->gbpTxPosition); + STORE_32(hw->gbpNextEvent, 0, &state->hw.gbpNextEvent); + STORE_32(flags1, 0, &state->hw.flags1); + STORE_32(flags2, 0, &state->hw.flags2); } void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) { - hw->readWrite = state->hw.readWrite; - hw->pinState = state->hw.pinState; - hw->direction = state->hw.pinDirection; + GBASerializedHWFlags1 flags1; + LOAD_16(flags1, 0, &state->hw.flags1); + hw->readWrite = GBASerializedHWFlags1GetReadWrite(flags1); + LOAD_16(hw->pinState, 0, &state->hw.pinState); + LOAD_16(hw->direction, 0, &state->hw.pinDirection); hw->devices = state->hw.devices; - hw->rtc = state->hw.rtc; - hw->gyroSample = state->hw.gyroSample; - hw->gyroEdge = state->hw.gyroEdge; - hw->tiltX = state->hw.tiltSampleX; - hw->tiltY = state->hw.tiltSampleY; - hw->tiltState = state->hw.tiltState; - hw->lightCounter = state->hw.lightCounter; + + LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtc.bytesRemaining); + LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtc.transferStep); + LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtc.bitsRead); + LOAD_32(hw->rtc.bits, 0, &state->hw.rtc.bits); + LOAD_32(hw->rtc.commandActive, 0, &state->hw.rtc.commandActive); + LOAD_32(hw->rtc.command, 0, &state->hw.rtc.command); + LOAD_32(hw->rtc.control, 0, &state->hw.rtc.control); + memcpy(hw->rtc.time, state->hw.rtc.time, sizeof(hw->rtc.time)); + + LOAD_16(hw->gyroSample, 0, &state->hw.gyroSample); + hw->gyroEdge = GBASerializedHWFlags1GetGyroEdge(flags1); + LOAD_16(hw->tiltX, 0, &state->hw.tiltSampleX); + LOAD_16(hw->tiltY, 0, &state->hw.tiltSampleY); + hw->tiltState = GBASerializedHWFlags2GetTiltState(state->hw.flags2); + hw->lightCounter = GBASerializedHWFlags1GetLightCounter(flags1); hw->lightSample = state->hw.lightSample; - hw->lightEdge = state->hw.lightEdge; - hw->gbpInputsPosted = state->hw.gbpInputsPosted; - hw->gbpTxPosition = state->hw.gbpTxPosition; - hw->gbpNextEvent = state->hw.gbpNextEvent; + hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1); + hw->gbpInputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2); + hw->gbpTxPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2); + LOAD_32(hw->gbpNextEvent, 0, &state->hw.gbpNextEvent); if (hw->devices & HW_GB_PLAYER) { GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32); } diff --git a/src/gba/hardware.h b/src/gba/hardware.h index 7923d0669..fa64a9075 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -98,7 +98,7 @@ DECL_BITFIELD(GPIOPin, uint16_t); struct GBACartridgeHardware { struct GBA* p; - int devices; + uint32_t devices; enum GPIODirection readWrite; uint16_t* gpioBase; diff --git a/src/gba/io.c b/src/gba/io.c index 04fd8ca7b..02652a238 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -690,21 +690,27 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { int i; for (i = 0; i < REG_MAX; i += 2) { if (_isSpecialRegister[i >> 1]) { - state->io[i >> 1] = gba->memory.io[i >> 1]; + STORE_16(gba->memory.io[i >> 1], i, state->io); } else if (_isValidRegister[i >> 1]) { - state->io[i >> 1] = GBAIORead(gba, i); + uint16_t reg = GBAIORead(gba, i); + STORE_16(reg, i, state->io); } } for (i = 0; i < 4; ++i) { - state->io[(REG_DMA0CNT_LO + i * 12) >> 1] = gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1]; - state->dma[i].nextSource = gba->memory.dma[i].nextSource; - state->dma[i].nextDest = gba->memory.dma[i].nextDest; - state->dma[i].nextCount = gba->memory.dma[i].nextCount; - state->dma[i].nextEvent = gba->memory.dma[i].nextEvent; + STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io); + STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload); + STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); + STORE_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent); + STORE_32(gba->timers[i].nextEvent, 0, &state->timers[i].nextEvent); + STORE_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval); + 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); + STORE_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); + STORE_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); } - memcpy(state->timers, gba->timers, sizeof(state->timers)); GBAHardwareSerialize(&gba->memory.hw, state); } @@ -712,25 +718,32 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { int i; for (i = 0; i < REG_MAX; i += 2) { if (_isSpecialRegister[i >> 1]) { - gba->memory.io[i >> 1] = state->io[i >> 1]; + LOAD_16(gba->memory.io[i >> 1], i, state->io); } else if (_isValidRegister[i >> 1]) { - GBAIOWrite(gba, i, state->io[i >> 1]); + uint16_t reg; + LOAD_16(reg, i, state->io); + GBAIOWrite(gba, i, reg); } } gba->timersEnabled = 0; - memcpy(gba->timers, state->timers, sizeof(gba->timers)); for (i = 0; i < 4; ++i) { - gba->memory.dma[i].reg = state->io[(REG_DMA0CNT_HI + i * 12) >> 1]; - gba->memory.dma[i].nextSource = state->dma[i].nextSource; - gba->memory.dma[i].nextDest = state->dma[i].nextDest; - gba->memory.dma[i].nextCount = state->dma[i].nextCount; - gba->memory.dma[i].nextEvent = state->dma[i].nextEvent; + LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload); + LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); + LOAD_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent); + LOAD_32(gba->timers[i].nextEvent, 0, &state->timers[i].nextEvent); + LOAD_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval); + LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags); + 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); + LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); + LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); + LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]); } - if (gba->timers[i].enable) { + if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { gba->timersEnabled |= 1 << i; } } diff --git a/src/gba/savedata.c b/src/gba/savedata.c index daa8aca68..de535aea1 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -444,13 +444,15 @@ void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) { void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData) { state->savedata.type = savedata->type; state->savedata.command = savedata->command; - state->savedata.flashState = savedata->flashState; - state->savedata.flashBank = savedata->currentBank == &savedata->data[0x10000]; - state->savedata.readBitsRemaining = savedata->readBitsRemaining; - state->savedata.readAddress = savedata->readAddress; - state->savedata.writeAddress = savedata->writeAddress; - state->savedata.settlingSector = savedata->settling; - state->savedata.settlingDust = savedata->dust; + GBASerializedSavedataFlags flags = 0; + flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState); + flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]); + state->savedata.flags = flags; + STORE_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining); + STORE_32(savedata->readAddress, 0, &state->savedata.readAddress); + STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress); + STORE_16(savedata->settling, 0, &state->savedata.settlingSector); + STORE_16(savedata->dust, 0, &state->savedata.settlingDust); UNUSED(includeData); // TODO } @@ -463,15 +465,16 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial GBASavedataForceType(savedata, state->savedata.type, savedata->realisticTiming); } savedata->command = state->savedata.command; - savedata->flashState = state->savedata.flashState; - savedata->readBitsRemaining = state->savedata.readBitsRemaining; - savedata->readAddress = state->savedata.readAddress; - savedata->writeAddress = state->savedata.writeAddress; - savedata->settling = state->savedata.settlingSector; - savedata->dust = state->savedata.settlingDust; + GBASerializedSavedataFlags flags = state->savedata.flags; + savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags); + LOAD_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining); + LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress); + LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress); + LOAD_16(savedata->settling, 0, &state->savedata.settlingSector); + LOAD_16(savedata->dust, 0, &state->savedata.settlingDust); if (savedata->type == SAVEDATA_FLASH1M) { - _flashSwitchBank(savedata, state->savedata.flashBank); + _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags)); } UNUSED(includeData); // TODO diff --git a/src/gba/serialize.c b/src/gba/serialize.c index c7fa7b016..3dd29c298 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -25,9 +25,9 @@ const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { - state->versionMagic = GBA_SAVESTATE_MAGIC; - state->biosChecksum = gba->biosChecksum; - state->romCrc32 = gba->romCrc32; + STORE_32(GBA_SAVESTATE_MAGIC, 0, &state->versionMagic); + STORE_32(gba->biosChecksum, 0, &state->biosChecksum); + STORE_32(gba->romCrc32, 0, &state->romCrc32); if (gba->memory.rom) { state->id = ((struct GBACartridge*) gba->memory.rom)->id; @@ -37,17 +37,25 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { memset(state->title, 0, sizeof(state->title)); } - memcpy(state->cpu.gprs, gba->cpu->gprs, sizeof(state->cpu.gprs)); - state->cpu.cpsr = gba->cpu->cpsr; - state->cpu.spsr = gba->cpu->spsr; - state->cpu.cycles = gba->cpu->cycles; - state->cpu.nextEvent = gba->cpu->nextEvent; - memcpy(state->cpu.bankedRegisters, gba->cpu->bankedRegisters, 6 * 7 * sizeof(int32_t)); - memcpy(state->cpu.bankedSPSRs, gba->cpu->bankedSPSRs, 6 * sizeof(int32_t)); + int i; + for (i = 0; i < 16; ++i) { + STORE_32(gba->cpu->gprs[i], i * sizeof(state->cpu.gprs[0]), state->cpu.gprs); + } + STORE_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed); + STORE_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed); + STORE_32(gba->cpu->cycles, 0, &state->cpu.cycles); + STORE_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent); + for (i = 0; i < 6; ++i) { + int j; + for (j = 0; j < 7; ++j) { + STORE_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters); + } + STORE_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs); + } state->biosPrefetch = gba->memory.biosPrefetch; - state->cpuPrefetch[0] = gba->cpu->prefetch[0]; - state->cpuPrefetch[1] = gba->cpu->prefetch[1]; + STORE_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch); + STORE_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch); GBAMemorySerialize(&gba->memory, state); GBAIOSerialize(gba, state); @@ -63,13 +71,19 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { bool error = false; - if (state->versionMagic != GBA_SAVESTATE_MAGIC) { - GBALog(gba, GBA_LOG_WARN, "Invalid or too new savestate"); + int32_t check; + uint32_t ucheck; + LOAD_32(ucheck, 0, &state->versionMagic); + if (ucheck != GBA_SAVESTATE_MAGIC) { + GBALog(gba, GBA_LOG_WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC, ucheck); error = true; } - if (state->biosChecksum != gba->biosChecksum) { - GBALog(gba, GBA_LOG_WARN, "Savestate created using a different version of the BIOS"); - if (state->cpu.gprs[ARM_PC] < SIZE_BIOS && state->cpu.gprs[ARM_PC] >= 0x20) { + LOAD_32(ucheck, 0, &state->biosChecksum); + if (ucheck != gba->biosChecksum) { + GBALog(gba, GBA_LOG_WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck); + uint32_t pc; + LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); + if (pc < SIZE_BIOS && pc >= 0x20) { error = true; } } @@ -80,46 +94,60 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBALog(gba, GBA_LOG_WARN, "Savestate is for a game, but no game loaded"); error = true; } - if (state->romCrc32 != gba->romCrc32) { + LOAD_32(ucheck, 0, &state->romCrc32); + if (ucheck != gba->romCrc32) { GBALog(gba, GBA_LOG_WARN, "Savestate is for a different version of the game"); } - if (state->cpu.cycles < 0) { + LOAD_32(check, 0, &state->cpu.cycles); + if (check < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are negative"); error = true; } - if (state->cpu.cycles >= (int32_t) GBA_ARM7TDMI_FREQUENCY) { + if (check >= (int32_t) GBA_ARM7TDMI_FREQUENCY) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are too high"); error = true; } - if (state->video.eventDiff < 0) { + LOAD_32(check, 0, &state->video.eventDiff); + if (check < 0) { GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: video eventDiff is negative"); error = true; } - int region = (state->cpu.gprs[ARM_PC] >> BASE_OFFSET); - if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((state->cpu.gprs[ARM_PC] - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) { + LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); + int region = (check >> BASE_OFFSET); + if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) { GBALog(gba, GBA_LOG_WARN, "Savestate created using a differently sized version of the ROM"); error = true; } if (error) { return false; } - memcpy(gba->cpu->gprs, state->cpu.gprs, sizeof(gba->cpu->gprs)); - gba->cpu->cpsr = state->cpu.cpsr; - gba->cpu->spsr = state->cpu.spsr; - gba->cpu->cycles = state->cpu.cycles; - gba->cpu->nextEvent = state->cpu.nextEvent; - memcpy(gba->cpu->bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t)); - memcpy(gba->cpu->bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t)); + size_t i; + for (i = 0; i < 16; ++i) { + LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs); + } + LOAD_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed); + LOAD_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed); + LOAD_32(gba->cpu->cycles, 0, &state->cpu.cycles); + LOAD_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent); + for (i = 0; i < 6; ++i) { + int j; + for (j = 0; j < 7; ++j) { + LOAD_32(gba->cpu->bankedRegisters[i][j], (i * 7 + j) * sizeof(gba->cpu->bankedRegisters[0][0]), state->cpu.bankedRegisters); + } + LOAD_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs); + } gba->cpu->privilegeMode = gba->cpu->cpsr.priv; gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); if (state->biosPrefetch) { - gba->memory.biosPrefetch = state->biosPrefetch; + LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch); } if (gba->cpu->cpsr.t) { gba->cpu->executionMode = MODE_THUMB; if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) { - gba->cpu->prefetch[0] = state->cpuPrefetch[0] & 0xFFFF; - gba->cpu->prefetch[1] = state->cpuPrefetch[1] & 0xFFFF; + LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch); + LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch); + gba->cpu->prefetch[0] &= 0xFFFF; + gba->cpu->prefetch[1] &= 0xFFFF; } else { // Maintain backwards compat LOAD_16(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion); @@ -128,8 +156,8 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { } else { gba->cpu->executionMode = MODE_ARM; if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) { - gba->cpu->prefetch[0] = state->cpuPrefetch[0]; - gba->cpu->prefetch[1] = state->cpuPrefetch[1]; + LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch); + LOAD_32(gba->cpu->prefetch[1], 4, state->cpuPrefetch); } else { // Maintain backwards compat LOAD_32(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion); @@ -245,10 +273,39 @@ bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, b bool success = GBASaveStateNamed(threadContext->gba, vf, screenshot); vf->close(vf); if (success) { +#if SAVESTATE_DEBUG + vf = GBAGetState(threadContext->gba, dir, slot, false); + if (vf) { + struct GBA* backup = anonymousMemoryMap(sizeof(*backup)); + memcpy(backup, threadContext->gba, sizeof(*backup)); + memset(threadContext->gba->memory.io, 0, sizeof(threadContext->gba->memory.io)); + memset(threadContext->gba->timers, 0, sizeof(threadContext->gba->timers)); + GBALoadStateNamed(threadContext->gba, vf); + if (memcmp(backup, threadContext->gba, sizeof(*backup))) { + char suffix[16] = { '\0' }; + struct VFile* vf2; + snprintf(suffix, sizeof(suffix), ".dump.0.%d", slot); + vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); + if (vf2) { + vf2->write(vf2, backup, sizeof(*backup)); + vf2->close(vf2); + } + snprintf(suffix, sizeof(suffix), ".dump.1.%d", slot); + vf2 = VDirOptionalOpenFile(dir, threadContext->gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); + if (vf2) { + vf2->write(vf2, threadContext->gba, sizeof(*threadContext->gba)); + vf2->close(vf2); + } + } + mappedMemoryFree(backup, sizeof(*backup)); + vf->close(vf); + } +#endif GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i saved", slot); } else { GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to save", slot); } + return success; } diff --git a/src/gba/serialize.h b/src/gba/serialize.h index 32b322a8c..e33fb1792 100644 --- a/src/gba/serialize.h +++ b/src/gba/serialize.h @@ -184,6 +184,33 @@ extern const uint32_t GBA_SAVESTATE_MAGIC; * Total size: 0x61000 (397,312) bytes */ +DECL_BITFIELD(GBASerializedAudioFlags, uint32_t); +DECL_BITS(GBASerializedAudioFlags, Ch1Volume, 0, 4); +DECL_BIT(GBASerializedAudioFlags, Ch1Dead, 4); +DECL_BIT(GBASerializedAudioFlags, Ch1Hi, 5); +DECL_BITS(GBASerializedAudioFlags, Ch2Volume, 8, 4); +DECL_BIT(GBASerializedAudioFlags, Ch2Dead, 12); +DECL_BIT(GBASerializedAudioFlags, Ch2Hi, 13); +DECL_BITS(GBASerializedAudioFlags, Ch4Volume, 16, 4); +DECL_BIT(GBASerializedAudioFlags, Ch4Dead, 20); + +DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); +DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0); +DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1); +DECL_BIT(GBASerializedHWFlags1, LightEdge, 2); +DECL_BITS(GBASerializedHWFlags1, LightCounter, 4, 12); + +DECL_BITFIELD(GBASerializedHWFlags2, uint8_t); +DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 2); +DECL_BITS(GBASerializedHWFlags2, GbpInputsPosted, 2, 2); +DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 5); + +DECL_BITFIELD(GBASerializedHWFlags3, uint16_t); + +DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); +DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); +DECL_BIT(GBASerializedSavedataFlags, FlashBank, 4); + struct GBASerializedState { uint32_t versionMagic; uint32_t biosChecksum; @@ -236,18 +263,7 @@ struct GBASerializedState { int32_t eventDiff; int32_t nextSample; uint32_t fifoSize; - unsigned ch1Volume : 4; - unsigned ch1Dead : 1; - unsigned ch1Hi : 1; - unsigned : 2; - unsigned ch2Volume : 4; - unsigned ch2Dead : 1; - unsigned ch2Hi : 1; - unsigned : 2; - unsigned ch4Volume : 4; - unsigned ch4Dead : 1; - unsigned : 3; - unsigned : 8; + GBASerializedAudioFlags flags; } audio; struct { @@ -275,33 +291,23 @@ struct GBASerializedState { uint16_t pinDirection; struct GBARTC rtc; uint8_t devices; - // Do not change these to uint16_t, this breaks bincompat with some older compilers - unsigned gyroSample : 16; - unsigned tiltSampleX : 16; - unsigned tiltSampleY : 16; - unsigned readWrite : 1; - unsigned gyroEdge : 1; - unsigned lightEdge : 1; - unsigned : 1; - unsigned lightCounter : 12; - unsigned lightSample : 8; - unsigned tiltState : 2; - unsigned gbpInputsPosted : 2; - unsigned gbpTxPosition : 5; - unsigned : 15; - uint32_t gbpNextEvent : 32; + uint16_t gyroSample; + uint16_t tiltSampleX; + uint16_t tiltSampleY; + GBASerializedHWFlags1 flags1; + uint8_t lightSample; + GBASerializedHWFlags2 flags2; + GBASerializedHWFlags3 flags3; + uint32_t gbpNextEvent; } hw; uint32_t reservedHardware[6]; struct { - unsigned type : 8; - unsigned command : 8; - unsigned flashState : 2; - unsigned : 2; - unsigned flashBank : 1; - unsigned : 3; - unsigned : 8; + uint8_t type; + uint8_t command; + GBASerializedSavedataFlags flags; + uint8_t reserved; int32_t readBitsRemaining; uint32_t readAddress; uint32_t writeAddress; diff --git a/src/gba/video.c b/src/gba/video.c index 8855c018a..505ef4fa5 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -274,14 +274,13 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* memcpy(state->vram, video->renderer->vram, SIZE_VRAM); memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); - state->video.nextEvent = video->nextEvent; - state->video.eventDiff = video->eventDiff; - state->video.lastHblank = video->nextHblank - VIDEO_HBLANK_LENGTH; - state->video.nextHblank = video->nextHblank; - state->video.nextHblankIRQ = video->nextHblankIRQ; - state->video.nextVblankIRQ = video->nextVblankIRQ; - state->video.nextVcounterIRQ = video->nextVcounterIRQ; - state->video.frameCounter = video->frameCounter; + STORE_32(video->nextEvent, 0, &state->video.nextEvent); + STORE_32(video->eventDiff, 0, &state->video.eventDiff); + STORE_32(video->nextHblank, 0, &state->video.nextHblank); + STORE_32(video->nextHblankIRQ, 0, &state->video.nextHblankIRQ); + STORE_32(video->nextVblankIRQ, 0, &state->video.nextVblankIRQ); + STORE_32(video->nextVcounterIRQ, 0, &state->video.nextVcounterIRQ); + STORE_32(video->frameCounter, 0, &state->video.frameCounter); } void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) { @@ -296,12 +295,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState LOAD_16(value, i, state->pram); GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0); } - video->nextEvent = state->video.nextEvent; - video->eventDiff = state->video.eventDiff; - video->nextHblank = state->video.nextHblank; - video->nextHblankIRQ = state->video.nextHblankIRQ; - video->nextVblankIRQ = state->video.nextVblankIRQ; - video->nextVcounterIRQ = state->video.nextVcounterIRQ; - video->frameCounter = state->video.frameCounter; - video->vcount = state->io[REG_VCOUNT >> 1]; + LOAD_32(video->nextEvent, 0, &state->video.nextEvent); + LOAD_32(video->eventDiff, 0, &state->video.eventDiff); + LOAD_32(video->nextHblank, 0, &state->video.nextHblank); + LOAD_32(video->nextHblankIRQ, 0, &state->video.nextHblankIRQ); + LOAD_32(video->nextVblankIRQ, 0, &state->video.nextVblankIRQ); + LOAD_32(video->nextVcounterIRQ, 0, &state->video.nextVcounterIRQ); + LOAD_32(video->frameCounter, 0, &state->video.frameCounter); + LOAD_16(video->vcount, REG_VCOUNT, state->io); } diff --git a/src/util/common.h b/src/util/common.h index 9ffb1ed22..ef83d61f0 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -86,6 +86,7 @@ typedef intptr_t ssize_t; #define INS_BITS(SRC, START, END, BITS) (CLEAR_BITS(SRC, START, END) | (((BITS) << (START)) & MAKE_MASK(START, END))) #define CLEAR_BITS(SRC, START, END) ((SRC) & ~MAKE_MASK(START, END)) #define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END)) +#define TEST_FILL_BITS(SRC, START, END, TEST) ((TEST) ? (FILL_BITS(SRC, START, END)) : (CLEAR_BITS(SRC, START, END))) #ifdef _MSC_VER #define ATTRIBUTE_UNUSED @@ -112,6 +113,9 @@ typedef intptr_t ssize_t; } \ ATTRIBUTE_UNUSED static inline TYPE TYPE ## Set ## FIELD (TYPE src, TYPE bits) { \ return INS_BITS(src, (START), (START) + (SIZE), bits); \ + } \ + ATTRIBUTE_UNUSED static inline TYPE TYPE ## TestFill ## FIELD (TYPE src, bool test) { \ + return TEST_FILL_BITS(src, (START), (START) + (SIZE), test); \ } #define DECL_BIT(TYPE, FIELD, BIT) DECL_BITS(TYPE, FIELD, BIT, 1)