mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/ppc-savestates'
This commit is contained in:
commit
ea204bbfed
117
src/gba/audio.c
117
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) {
|
||||
|
|
|
@ -128,7 +128,7 @@ struct GBAAudioChannel4 {
|
|||
int32_t endTime;
|
||||
} control;
|
||||
|
||||
unsigned lfsr;
|
||||
uint32_t lfsr;
|
||||
int8_t sample;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
47
src/gba/io.c
47
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue