From 06e0908642eb0e6c4d151c276f9976d7b1190862 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 20 Jan 2016 20:09:07 -0800 Subject: [PATCH] GB: Start on timers --- src/gb/gb.c | 8 +++++ src/gb/gb.h | 2 ++ src/gb/io.c | 17 ++++++++++ src/gb/timer.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/gb/timer.h | 38 +++++++++++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 src/gb/timer.c create mode 100644 src/gb/timer.h diff --git a/src/gb/gb.c b/src/gb/gb.c index 703e79003..c3de0599d 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -41,6 +41,8 @@ static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) gb->video.p = gb; GBVideoInit(&gb->video); + gb->timer.p = gb; + gb->romVf = 0; gb->pristineRom = 0; @@ -126,6 +128,7 @@ void GBReset(struct LR35902Core* cpu) { } GBMemoryReset(gb); GBVideoReset(&gb->video); + GBTimerReset(&gb->timer); GBIOReset(gb); } @@ -176,6 +179,11 @@ void GBProcessEvents(struct LR35902Core* cpu) { nextEvent = testEvent; } + testEvent = GBTimerProcessEvents(&gb->timer, cycles); + if (testEvent < nextEvent) { + nextEvent = testEvent; + } + cpu->cycles -= cycles; cpu->nextEvent = nextEvent; } diff --git a/src/gb/gb.h b/src/gb/gb.h index 680e807bd..9db0db0af 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -11,6 +11,7 @@ #include "lr35902/lr35902.h" #include "gb/memory.h" +#include "gb/timer.h" #include "gb/video.h" extern const uint32_t DMG_LR35902_FREQUENCY; @@ -40,6 +41,7 @@ struct GB { struct LR35902Core* cpu; struct GBMemory memory; struct GBVideo video; + struct GBTimer timer; int* keySource; diff --git a/src/gb/io.c b/src/gb/io.c index 763c18e77..e7f669663 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -50,6 +50,18 @@ void GBIOReset(struct GB* gb) { void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { switch (address) { + case REG_DIV: + GBTimerDivReset(&gb->timer); + return; + case REG_TIMA: + // ??? + return; + case REG_TMA: + // Handled transparently by the registers + break; + case REG_TAC: + value = GBTimerUpdateTAC(&gb->timer, value); + break; case REG_IF: gb->memory.io[REG_IF] = value; GBUpdateIRQs(gb); @@ -108,6 +120,11 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { break; case REG_IE: return gb->memory.ie; + case REG_DIV: + case REG_TIMA: + case REG_TMA: + case REG_TAC: + break; default: // TODO: Log if (address >= GB_SIZE_IO) { diff --git a/src/gb/timer.c b/src/gb/timer.c new file mode 100644 index 000000000..db6c149e4 --- /dev/null +++ b/src/gb/timer.c @@ -0,0 +1,84 @@ +/* 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 "timer.h" + +#include "gb/gb.h" +#include "gb/io.h" + +void GBTimerReset(struct GBTimer* timer) { + timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences + timer->nextTima = INT_MAX; + timer->nextEvent = INT_MAX; +} + +int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) { + if (timer->nextEvent == INT_MAX) { + return INT_MAX; + } + timer->eventDiff += cycles; + timer->nextEvent -= cycles; + if (timer->nextEvent < 0) { + timer->nextDiv -= timer->eventDiff; + if (timer->nextDiv <= 0) { + ++timer->p->memory.io[REG_DIV]; + timer->nextDiv = GB_DMG_DIV_PERIOD; + } + timer->nextEvent = timer->nextDiv; + + if (timer->nextTima != INT_MAX) { + timer->nextTima -= timer->eventDiff; + if (timer->nextTima <= 0) { + ++timer->p->memory.io[REG_TIMA]; + if (!timer->p->memory.io[REG_TIMA]) { + 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->nextTima = timer->timaPeriod; + } + if (timer->nextTima < timer->nextEvent) { + timer->nextEvent = timer->nextTima; + } + } + + timer->eventDiff = 0; + } + return timer->nextEvent; +} + +void GBTimerDivReset(struct GBTimer* timer) { + timer->p->memory.io[REG_DIV] = 0; + // TODO: Do we need to reset the event? +} + +uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) { + if (GBRegisterTACIsRun(tac)) { + switch (GBRegisterTACGetClock(tac)) { + case 0: + timer->timaPeriod = 1024; + break; + case 1: + timer->timaPeriod = 16; + break; + case 2: + timer->timaPeriod = 64; + break; + case 3: + timer->timaPeriod = 256; + break; + } + timer->nextTima = timer->eventDiff + timer->timaPeriod; + if (timer->nextTima < timer->nextEvent) { + timer->nextEvent = timer->nextTima; + if (timer->nextEvent < timer->p->cpu->nextEvent) { + timer->p->cpu->nextEvent = timer->nextEvent; + } + } + } else { + timer->nextTima = INT_MAX; + } + return tac; +} diff --git a/src/gb/timer.h b/src/gb/timer.h new file mode 100644 index 000000000..19f4bcf80 --- /dev/null +++ b/src/gb/timer.h @@ -0,0 +1,38 @@ +/* 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 GB_TIMER_H +#define GB_TIMER_H + +#include "util/common.h" + +DECL_BITFIELD(GBRegisterTAC, uint8_t); +DECL_BITS(GBRegisterTAC, Clock, 0, 2); +DECL_BIT(GBRegisterTAC, Run, 2); + +enum { + GB_DMG_DIV_PERIOD = 256 +}; + +struct GB; +struct GBTimer { + struct GB* p; + + int mode; + + int32_t nextEvent; + int32_t eventDiff; + + int32_t nextDiv; + int32_t nextTima; + int32_t timaPeriod; +}; + +void GBTimerReset(struct GBTimer*); +int32_t GBTimerProcessEvents(struct GBTimer*, int32_t cycles); +void GBTimerDivReset(struct GBTimer*); +uint8_t GBTimerUpdateTAC(struct GBTimer*, GBRegisterTAC tac); + +#endif