Merge branch 'feature/ppc-savestates'

This commit is contained in:
Jeffrey Pfau 2015-10-19 22:05:48 -07:00
commit ea204bbfed
13 changed files with 355 additions and 239 deletions

View File

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

View File

@ -128,7 +128,7 @@ struct GBAAudioChannel4 {
int32_t endTime;
} control;
unsigned lfsr;
uint32_t lfsr;
int8_t sample;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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