GB: Start on timers

This commit is contained in:
Jeffrey Pfau 2016-01-20 20:09:07 -08:00
parent 4b51783589
commit 06e0908642
5 changed files with 149 additions and 0 deletions

View File

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

View File

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

View File

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

84
src/gb/timer.c Normal file
View File

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

38
src/gb/timer.h Normal file
View File

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