Skip to content

Commit

Permalink
Greatly improve the stability of quicksaves.
Browse files Browse the repository at this point in the history
  • Loading branch information
Arignir committed Dec 14, 2023
1 parent 55649c9 commit dd06928
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 64 deletions.
1 change: 1 addition & 0 deletions include/gba/apu.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ void apu_resample(struct gba *gba, struct event_args args);
/* gba/apu/wave.c */
void apu_wave_reset(struct gba *gba);
void apu_wave_stop(struct gba *gba);
void apu_wave_step(struct gba *gba, struct event_args args);
4 changes: 3 additions & 1 deletion include/gba/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "hades.h"
#include "gba/memory.h"

#define NO_CURRENT_DMA ((ssize_t)-1)

struct gba;

enum core_states {
Expand Down Expand Up @@ -117,7 +119,7 @@ struct core {
enum core_states state; // 0=Run, 1=Halt, 2=Stop

bool is_dma_running; // Set to `true` when waiting for a DMA to complete.
struct dma_channel *current_dma; // The DMA the core is currently waiting for. Can be NULL.
ssize_t current_dma_idx; // The DMA the core is currently waiting for. `NO_CURRENT_DMA` if no DMA is running.

uint32_t pending_dma; // A mask of all DMA's index waiting for transfer
bool reenter_dma_transfer_loop;
Expand Down
2 changes: 2 additions & 0 deletions include/gba/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,8 @@ void io_scan_keypad_irq(struct gba *gba);
char const *mem_io_reg_name(uint32_t addr);

/* gba/timer.c */
void timer_stop(struct gba *gba, struct event_args args);
void timer_overflow(struct gba *gba, struct event_args args);
void timer_schedule_start(struct gba *gba, uint32_t timer_idx);
void timer_schedule_stop(struct gba *gba, uint32_t timer_idx);
uint16_t timer_update_counter(struct gba const *gba, uint32_t timer_idx);
Expand Down
2 changes: 2 additions & 0 deletions include/gba/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <stdint.h>
#include "hades.h"
#include "gba/scheduler.h"

/*
** Access to the memory bus can either be sequential (the requested address follows the previous one)
Expand Down Expand Up @@ -264,6 +265,7 @@ bool mem_dma_is_fifo(struct gba const *gba, uint32_t dma_channel_idx, uint32_t f
void mem_schedule_dma_transfers_for(struct gba *gba, uint32_t channel_idx, enum dma_timings timing);
void mem_schedule_dma_transfers(struct gba *gba, enum dma_timings timing);
void mem_dma_do_all_pending_transfers(struct gba *gba);
void mem_dma_add_to_pending(struct gba *gba, struct event_args args);

/* gba/memory/io.c */
uint8_t mem_io_read8(struct gba const *gba, uint32_t addr);
Expand Down
34 changes: 21 additions & 13 deletions include/gba/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@
typedef size_t event_handler_t;

enum sched_event_kind {
EVENT_HBLANK,
EVENT_HDRAW,
EVENT_APU,
SCHED_EVENT_FRAME_LIMITER,
SCHED_EVENT_PPU_HDRAW,
SCHED_EVENT_PPU_HBLANK,
SCHED_EVENT_TIMER_OVERFLOW,
SCHED_EVENT_TIMER_STOP,
SCHED_EVENT_APU_SEQUENCER,
SCHED_EVENT_APU_RESAMPLE,
SCHED_EVENT_APU_WAVE_STEP,
SCHED_EVENT_DMA_ADD_PENDING,
};

enum sched_event_type {
Expand All @@ -39,6 +45,8 @@ struct event_args {
};

struct scheduler_event {
enum sched_event_kind kind;

bool active;
bool repeat;

Expand All @@ -47,8 +55,6 @@ struct scheduler_event {

// The "argument" given to the event callback.
struct event_args args;

void (*callback)(struct gba *gba, struct event_args args);
};

struct scheduler {
Expand All @@ -66,44 +72,44 @@ struct scheduler {
uint64_t accumulated_time;
};

#define NEW_FIX_EVENT(_at, _callback) \
#define NEW_FIX_EVENT(_kind, _at) \
(struct scheduler_event){ \
.kind = (_kind), \
.active = true, \
.repeat = false, \
.at = (_at), \
.period = 0, \
.args = (struct event_args){{ 0 }}, \
.callback = (_callback), \
}

#define NEW_FIX_EVENT_ARGS(_at, _callback, ...) \
#define NEW_FIX_EVENT_ARGS(_kind, _at, ...) \
(struct scheduler_event){ \
.kind = (_kind), \
.active = true, \
.repeat = false, \
.at = (_at), \
.period = 0, \
.args = EVENT_ARGS(__VA_ARGS__), \
.callback = (_callback), \
}

#define NEW_REPEAT_EVENT(_at, _period, _callback) \
#define NEW_REPEAT_EVENT(_kind, _at, _period) \
(struct scheduler_event){ \
.kind = (_kind), \
.active = true, \
.repeat = true, \
.at = (_at), \
.period = (_period), \
.args = (struct event_args){{ 0 }}, \
.callback = (_callback), \
}

#define NEW_REPEAT_EVENT_ARGS(_at, _period, _callback, ...) \
#define NEW_REPEAT_EVENT_ARGS(_kind, _at, _period, ...) \
(struct scheduler_event){ \
.kind = (_kind), \
.active = true, \
.repeat = true, \
.at = (_at), \
.period = (_period), \
.args = EVENT_ARGS(__VA_ARGS__), \
.callback = (_callback), \
}

#define EVENT_ARGS_1(_1) ((struct event_args) { .a1 = (_1), .a2 = EVENT_ARG_EMPTY })
Expand All @@ -114,6 +120,8 @@ struct scheduler {
#define EVENT_ARG(kind, _value) ((union event_arg) { .kind = (_value) })
#define EVENT_ARG_EMPTY ((union event_arg) { 0 })

struct gba;

/* gba/scheduler.c */
event_handler_t sched_add_event(struct gba *gba, struct scheduler_event event);
void sched_cancel_event(struct gba *gba, event_handler_t handler);
Expand Down
5 changes: 2 additions & 3 deletions source/gba/apu/wave.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ apu_wave_reset(
gba->apu.wave.step_handler = sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_APU_WAVE_STEP,
gba->scheduler.cycles, // TODO: Is there a delay before the sound is started?
period,
apu_wave_step
period
)
);
}
Expand Down Expand Up @@ -75,7 +75,6 @@ apu_wave_stop(
** Shift the wave bank and store the 4 least significant bits
** into `gba->apu.latch.wave`.
*/
static
void
apu_wave_step(
struct gba *gba,
Expand Down
20 changes: 10 additions & 10 deletions source/gba/gba.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ gba_state_reset(
sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_FRAME_LIMITER,
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH * GBA_SCREEN_REAL_HEIGHT, // Timing of first trigger
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH * GBA_SCREEN_REAL_HEIGHT, // Period
sched_frame_limiter
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH * GBA_SCREEN_REAL_HEIGHT // Period
)
);
}
Expand Down Expand Up @@ -226,19 +226,19 @@ gba_state_reset(
sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_APU_SEQUENCER,
0,
GBA_CYCLES_PER_SECOND / 256,
apu_sequencer
GBA_CYCLES_PER_SECOND / 256
)
);

if (config->audio_frequency) {
sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_APU_RESAMPLE,
0,
config->audio_frequency,
apu_resample
config->audio_frequency
)
);
}
Expand All @@ -255,19 +255,19 @@ gba_state_reset(
sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_PPU_HDRAW,
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH, // Timing of first trigger
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH, // Period
ppu_hdraw
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH // Period
)
);

// HBlank
sched_add_event(
gba,
NEW_REPEAT_EVENT(
SCHED_EVENT_PPU_HBLANK,
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_WIDTH + 46, // Timing of first trigger
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH, // Period
ppu_hblank
GBA_CYCLES_PER_PIXEL * GBA_SCREEN_REAL_WIDTH // Period
)
);
}
Expand Down
7 changes: 3 additions & 4 deletions source/gba/memory/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,9 @@ mem_dma_do_all_pending_transfers(
continue;
}

gba->core.current_dma = channel;
gba->core.current_dma_idx = i;
dma_run_channel(gba, channel);
gba->core.current_dma = NULL;
gba->core.current_dma_idx = NO_CURRENT_DMA;
break;
}
}
Expand All @@ -232,7 +232,6 @@ mem_dma_do_all_pending_transfers(
gba->core.is_dma_running = false;
}

static
void
mem_dma_add_to_pending(
struct gba *gba,
Expand Down Expand Up @@ -261,8 +260,8 @@ mem_schedule_dma_transfers_for(
channel->enable_event_handle = sched_add_event(
gba,
NEW_FIX_EVENT_ARGS(
SCHED_EVENT_DMA_ADD_PENDING,
gba->scheduler.cycles + 2,
mem_dma_add_to_pending,
EVENT_ARG(u32, channel_idx)
)
);
Expand Down
7 changes: 5 additions & 2 deletions source/gba/memory/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,11 @@ mem_openbus_read(

shift = addr & 0x3;

if (gba->core.current_dma) {
return (gba->core.current_dma->bus >> (8 * shift));
if (gba->core.current_dma_idx != NO_CURRENT_DMA) {
struct dma_channel const *channel;

channel = &gba->io.dma[gba->core.current_dma_idx];
return (channel->bus >> (8 * shift));
}

if (gba->core.cpsr.thumb) {
Expand Down
43 changes: 17 additions & 26 deletions source/gba/quicksave.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,21 @@ quicksave(
buffer.index = 0;

quicksave_write(&buffer, (uint8_t *)&gba->core, sizeof(gba->core));
quicksave_write(&buffer, (uint8_t *)gba->memory.ewram, sizeof(gba->memory.ewram));
quicksave_write(&buffer, (uint8_t *)gba->memory.iwram, sizeof(gba->memory.iwram));
quicksave_write(&buffer, (uint8_t *)gba->memory.palram, sizeof(gba->memory.palram));
quicksave_write(&buffer, (uint8_t *)gba->memory.vram, sizeof(gba->memory.vram));
quicksave_write(&buffer, (uint8_t *)gba->memory.oam, sizeof(gba->memory.oam));
quicksave_write(&buffer, (uint8_t *)&gba->memory.backup_storage.chip, sizeof(gba->memory.backup_storage.chip));
quicksave_write(&buffer, (uint8_t *)&gba->memory.pbuffer, sizeof(gba->memory.pbuffer));
quicksave_write(&buffer, (uint8_t *)&gba->memory.bios_bus, sizeof(gba->memory.bios_bus));
quicksave_write(&buffer, (uint8_t *)&gba->memory.gamepak_bus_in_use, sizeof(gba->memory.gamepak_bus_in_use));
quicksave_write(&buffer, (uint8_t *)&gba->memory, sizeof(gba->memory));
quicksave_write(&buffer, (uint8_t *)&gba->io, sizeof(gba->io));
quicksave_write(&buffer, (uint8_t *)&gba->ppu, sizeof(gba->ppu));
quicksave_write(&buffer, (uint8_t *)&gba->gpio, sizeof(gba->gpio));
quicksave_write(&buffer, (uint8_t *)&gba->apu.fifos, sizeof(gba->apu.fifos));
quicksave_write(&buffer, (uint8_t *)&gba->apu.wave, sizeof(gba->apu.wave));
quicksave_write(&buffer, (uint8_t *)&gba->apu.latch, sizeof(gba->apu.latch));
quicksave_write(&buffer, (uint8_t *)&gba->apu, sizeof(gba->apu));
quicksave_write(&buffer, (uint8_t *)&gba->scheduler.cycles, sizeof(uint64_t));
quicksave_write(&buffer, (uint8_t *)&gba->scheduler.next_event, sizeof(uint64_t));
quicksave_write(&buffer, (uint8_t *)&gba->scheduler.events_size, sizeof(size_t));

// Serialize the scheduler's event list
for (i = 0; i < gba->scheduler.events_size; ++i) {
struct scheduler_event *event;

event = gba->scheduler.events + i;
quicksave_write(&buffer, (uint8_t *)&event->kind, sizeof(enum sched_event_kind));
quicksave_write(&buffer, (uint8_t *)&event->active, sizeof(bool));
quicksave_write(&buffer, (uint8_t *)&event->repeat, sizeof(bool));
quicksave_write(&buffer, (uint8_t *)&event->at, sizeof(uint64_t));
Expand Down Expand Up @@ -124,36 +116,35 @@ quickload(
buffer.size = size;
buffer.index = 0;

free(gba->scheduler.events);
gba->scheduler.events = NULL;
gba->scheduler.events_size = 0;

if (
quicksave_read(&buffer, (uint8_t *)&gba->core, sizeof(gba->core))
|| quicksave_read(&buffer, (uint8_t *)gba->memory.ewram, sizeof(gba->memory.ewram))
|| quicksave_read(&buffer, (uint8_t *)gba->memory.iwram, sizeof(gba->memory.iwram))
|| quicksave_read(&buffer, (uint8_t *)gba->memory.palram, sizeof(gba->memory.palram))
|| quicksave_read(&buffer, (uint8_t *)gba->memory.vram, sizeof(gba->memory.vram))
|| quicksave_read(&buffer, (uint8_t *)gba->memory.oam, sizeof(gba->memory.oam))
|| quicksave_read(&buffer, (uint8_t *)&gba->memory.backup_storage.chip, sizeof(gba->memory.backup_storage.chip))
|| quicksave_read(&buffer, (uint8_t *)&gba->memory.pbuffer, sizeof(gba->memory.pbuffer))
|| quicksave_read(&buffer, (uint8_t *)&gba->memory.bios_bus, sizeof(gba->memory.bios_bus))
|| quicksave_read(&buffer, (uint8_t *)&gba->memory.gamepak_bus_in_use, sizeof(gba->memory.gamepak_bus_in_use))
|| quicksave_read(&buffer, (uint8_t *)&gba->memory, sizeof(gba->memory))
|| quicksave_read(&buffer, (uint8_t *)&gba->io, sizeof(gba->io))
|| quicksave_read(&buffer, (uint8_t *)&gba->ppu, sizeof(gba->ppu))
|| quicksave_read(&buffer, (uint8_t *)&gba->gpio, sizeof(gba->gpio))
|| quicksave_read(&buffer, (uint8_t *)&gba->apu.fifos, sizeof(gba->apu.fifos))
|| quicksave_read(&buffer, (uint8_t *)&gba->apu.wave, sizeof(gba->apu.wave))
|| quicksave_read(&buffer, (uint8_t *)&gba->apu.latch, sizeof(gba->apu.latch))
|| quicksave_read(&buffer, (uint8_t *)&gba->apu, sizeof(gba->apu))
|| quicksave_read(&buffer, (uint8_t *)&gba->scheduler.cycles, sizeof(uint64_t))
|| quicksave_read(&buffer, (uint8_t *)&gba->scheduler.next_event, sizeof(uint64_t))
|| quicksave_read(&buffer, (uint8_t *)&gba->scheduler.events_size, sizeof(size_t))
) {
return (true);
}

gba->scheduler.events = calloc(gba->scheduler.events_size, sizeof(struct scheduler_event));
hs_assert(gba->scheduler.events);

// Serialize the scheduler's event list
for (i = 0; i < gba->scheduler.events_size; ++i) {
struct scheduler_event *event;

event = gba->scheduler.events + i;
event = &gba->scheduler.events[i];
if (
quicksave_read(&buffer, (uint8_t *)&event->active, sizeof(bool))
quicksave_read(&buffer, (uint8_t *)&event->kind, sizeof(enum sched_event_kind))
|| quicksave_read(&buffer, (uint8_t *)&event->active, sizeof(bool))
|| quicksave_read(&buffer, (uint8_t *)&event->repeat, sizeof(bool))
|| quicksave_read(&buffer, (uint8_t *)&event->at, sizeof(uint64_t))
|| quicksave_read(&buffer, (uint8_t *)&event->period, sizeof(uint64_t))
Expand Down
14 changes: 13 additions & 1 deletion source/gba/scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
#include "gba/memory.h"
#include "common/compat.h"

void (*sched_event_callbacks[])(struct gba *gba, struct event_args args) = {
[SCHED_EVENT_FRAME_LIMITER] = sched_frame_limiter,
[SCHED_EVENT_PPU_HDRAW] = ppu_hdraw,
[SCHED_EVENT_PPU_HBLANK] = ppu_hblank,
[SCHED_EVENT_TIMER_OVERFLOW] = timer_overflow,
[SCHED_EVENT_TIMER_STOP] = timer_stop,
[SCHED_EVENT_APU_SEQUENCER] = apu_sequencer,
[SCHED_EVENT_APU_RESAMPLE] = apu_resample,
[SCHED_EVENT_APU_WAVE_STEP] = apu_wave_step,
[SCHED_EVENT_DMA_ADD_PENDING] = mem_dma_add_to_pending,
};

void
sched_process_events(
struct gba *gba
Expand Down Expand Up @@ -67,7 +79,7 @@ sched_process_events(
event->active = false;
}

event->callback(gba, event->args);
sched_event_callbacks[event->kind](gba, event->args);
scheduler->cycles += delay;
}
}
Expand Down
Loading

0 comments on commit dd06928

Please sign in to comment.