mirror of https://github.com/mgba-emu/mgba.git
GB: Start on timers
This commit is contained in:
parent
4b51783589
commit
06e0908642
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
17
src/gb/io.c
17
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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue