Core, GB: Start new timing interface

This commit is contained in:
Jeffrey Pfau 2016-09-17 18:19:09 -07:00
parent c662d779a6
commit d831de205f
6 changed files with 165 additions and 72 deletions

58
src/core/timing.c Normal file
View File

@ -0,0 +1,58 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "timing.h"
DEFINE_VECTOR(mTimingEventList, struct mTimingEvent*);
void mTimingInit(struct mTiming* timing) {
mTimingEventListInit(&timing->events, 0);
timing->masterCycles = 0;
}
void mTimingDeinit(struct mTiming* timing) {
mTimingEventListDeinit(&timing->events);
}
void mTimingClear(struct mTiming* timing) {
mTimingEventListClear(&timing->events);
timing->masterCycles = 0;
}
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t when) {
event->when = when + timing->masterCycles;
size_t e;
for (e = 0; e < mTimingEventListSize(&timing->events); ++e) {
struct mTimingEvent* next = *mTimingEventListGetPointer(&timing->events, e);
int32_t nextWhen = next->when - timing->masterCycles;
if (nextWhen > when) {
mTimingEventListUnshift(&timing->events, e, 1);
*mTimingEventListGetPointer(&timing->events, e) = event;
return;
}
}
*mTimingEventListAppend(&timing->events) = event;
}
void mTimingTick(struct mTiming* timing, int32_t cycles) {
timing->masterCycles += cycles;
while (mTimingEventListSize(&timing->events)) {
struct mTimingEvent* next = *mTimingEventListGetPointer(&timing->events, 0);
int32_t nextWhen = next->when - timing->masterCycles;
if (nextWhen > 0) {
return;
}
mTimingEventListShift(&timing->events, 0, 1);
next->callback(timing, next->context, -nextWhen);
}
}
int32_t mTimingNextEvent(struct mTiming* timing) {
if (!mTimingEventListSize(&timing->events)) {
return INT_MAX;
}
struct mTimingEvent* next = *mTimingEventListGetPointer(&timing->events, 0);
return next->when - timing->masterCycles;
}

35
src/core/timing.h Normal file
View File

@ -0,0 +1,35 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef M_CORE_TIMING
#define M_CORE_TIMING
#include "util/common.h"
#include "util/vector.h"
struct mTiming;
struct mTimingEvent {
void* context;
void (*callback)(struct mTiming*, void* context, uint32_t);
const char* name;
uint32_t when;
};
DECLARE_VECTOR(mTimingEventList, struct mTimingEvent*);
struct mTiming {
struct mTimingEventList events;
uint32_t masterCycles;
};
void mTimingInit(struct mTiming* timing);
void mTimingDeinit(struct mTiming* timing);
void mTimingClear(struct mTiming* timing);
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
void mTimingTick(struct mTiming* timing, int32_t cycles);
int32_t mTimingNextEvent(struct mTiming* timing);
#endif

View File

@ -32,6 +32,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
mLOG_DEFINE_CATEGORY(GB, "GB");
static void GBInit(void* cpu, struct mCPUComponent* component);
static void GBDeinit(struct mCPUComponent* component);
static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
static void GBProcessEvents(struct LR35902Core* cpu);
static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
@ -46,7 +47,7 @@ extern size_t romBufferSize;
void GBCreate(struct GB* gb) {
gb->d.id = GB_COMPONENT_MAGIC;
gb->d.init = GBInit;
gb->d.deinit = 0;
gb->d.deinit = GBDeinit;
}
static void GBInit(void* cpu, struct mCPUComponent* component) {
@ -81,6 +82,13 @@ static void GBInit(void* cpu, struct mCPUComponent* component) {
gb->coreCallbacks = NULL;
gb->stream = NULL;
mTimingInit(&gb->timing);
}
static void GBDeinit(struct mCPUComponent* component) {
struct GB* gb = (struct GB*) component;
mTimingDeinit(&gb->timing);
}
bool GBLoadROM(struct GB* gb, struct VFile* vf) {
@ -425,9 +433,14 @@ void GBReset(struct LR35902Core* cpu) {
gb->memory.romSize = gb->yankedRomSize;
gb->yankedRomSize = 0;
}
mTimingClear(&gb->timing);
GBMemoryReset(gb);
GBVideoReset(&gb->video);
GBTimerReset(&gb->timer);
mTimingSchedule(&gb->timing, &gb->timer.event, GB_DMG_DIV_PERIOD);
GBAudioReset(&gb->audio);
GBIOReset(gb);
GBSIOReset(&gb->sio);
@ -530,6 +543,12 @@ void GBProcessEvents(struct LR35902Core* cpu) {
}
}
mTimingTick(&gb->timing, cycles);
testEvent = mTimingNextEvent(&gb->timing);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
if (testEvent != INT_MAX) {
testEvent <<= gb->doubleSpeed;
@ -546,11 +565,6 @@ void GBProcessEvents(struct LR35902Core* cpu) {
}
}
testEvent = GBTimerProcessEvents(&gb->timer, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
testEvent = GBSIOProcessEvents(&gb->sio, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;

View File

@ -9,6 +9,7 @@
#include "util/common.h"
#include "core/log.h"
#include "core/timing.h"
#include "lr35902/lr35902.h"
@ -57,6 +58,7 @@ struct GB {
enum GBModel model;
struct mCoreSync* sync;
struct mTiming timing;
uint8_t* keySource;

View File

@ -9,68 +9,62 @@
#include "gb/io.h"
#include "gb/serialize.h"
void GBTimerReset(struct GBTimer* timer) {
timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
timer->nextEvent = GB_DMG_DIV_PERIOD;
timer->eventDiff = 0;
timer->timaPeriod = 1024 >> 4;
timer->internalDiv = 0;
void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBTimer* timer = context;
timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
GBUpdateIRQs(timer->p);
}
int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
timer->eventDiff += cycles;
timer->nextEvent -= cycles;
if (timer->nextEvent <= 0) {
timer->nextDiv -= timer->eventDiff;
if (timer->irqPending) {
timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
GBUpdateIRQs(timer->p);
timer->irqPending = false;
timer->nextEvent += timer->nextDiv;
}
while (timer->nextDiv <= 0) {
timer->nextDiv += GB_DMG_DIV_PERIOD;
void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBTimer* timer = context;
timer->nextDiv += cyclesLate;
while (timer->nextDiv > 0) {
timer->nextDiv -= GB_DMG_DIV_PERIOD;
// Make sure to trigger when the correct bit is a falling edge
if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) {
timer->irqPending = true;
timer->nextEvent += 4;
}
// Make sure to trigger when the correct bit is a falling edge
if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) {
mTimingSchedule(timing, &timer->irq, 4 - cyclesLate);
}
++timer->internalDiv;
timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
}
if (timer->nextEvent <= 0) {
// Batch div increments
int divsToGo = 16 - (timer->internalDiv & 15);
int timaToGo = INT_MAX;
if (timer->timaPeriod) {
timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
}
if (timaToGo < divsToGo) {
divsToGo = timaToGo;
}
timer->nextEvent += GB_DMG_DIV_PERIOD * divsToGo;
}
timer->eventDiff = 0;
++timer->internalDiv;
timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
}
return timer->nextEvent;
// Batch div increments
int divsToGo = 16 - (timer->internalDiv & 15);
int timaToGo = INT_MAX;
if (timer->timaPeriod) {
timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
}
if (timaToGo < divsToGo) {
divsToGo = timaToGo;
}
timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo;
mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate);
}
void GBTimerReset(struct GBTimer* timer) {
timer->event.context = timer;
timer->event.name = "GB Timer";
timer->event.callback = _GBTimerIncrement;
timer->irq.context = timer;
timer->irq.name = "GB Timer IRQ";
timer->irq.callback = _GBTimerIRQ;
timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
timer->timaPeriod = 1024 >> 4;
timer->internalDiv = 0;
}
void GBTimerDivReset(struct GBTimer* timer) {
timer->p->memory.io[REG_DIV] = 0;
timer->internalDiv = 0;
timer->nextDiv = timer->p->cpu->cycles + GB_DMG_DIV_PERIOD;
if (timer->nextDiv < timer->nextEvent) {
timer->nextEvent = timer->nextDiv;
}
if (timer->nextDiv < timer->p->cpu->nextEvent) {
timer->p->cpu->nextEvent = timer->nextDiv;
}
timer->nextDiv += timer->eventDiff;
timer->nextDiv = GB_DMG_DIV_PERIOD;
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
}
uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
@ -94,25 +88,15 @@ uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
}
return tac;
}
void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
GBSerializedTimerFlags flags = 0;
flags = GBSerializedTimerFlagsSetIrqPending(flags, state->timer.flags);
state->timer.flags = flags;
}
void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
GBSerializedTimerFlags flags = state->timer.flags ;
timer->irqPending = GBSerializedTimerFlagsIsIrqPending(flags);
}

View File

@ -8,6 +8,8 @@
#include "util/common.h"
#include "core/timing.h"
DECL_BITFIELD(GBRegisterTAC, uint8_t);
DECL_BITS(GBRegisterTAC, Clock, 0, 2);
DECL_BIT(GBRegisterTAC, Run, 2);
@ -20,17 +22,15 @@ struct GB;
struct GBTimer {
struct GB* p;
int32_t nextEvent;
int32_t eventDiff;
struct mTimingEvent event;
struct mTimingEvent irq;
uint32_t internalDiv;
int32_t nextDiv;
uint32_t timaPeriod;
bool irqPending;
};
void GBTimerReset(struct GBTimer*);
int32_t GBTimerProcessEvents(struct GBTimer*, int32_t cycles);
void GBTimerDivReset(struct GBTimer*);
uint8_t GBTimerUpdateTAC(struct GBTimer*, GBRegisterTAC tac);