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;
|
gb->video.p = gb;
|
||||||
GBVideoInit(&gb->video);
|
GBVideoInit(&gb->video);
|
||||||
|
|
||||||
|
gb->timer.p = gb;
|
||||||
|
|
||||||
gb->romVf = 0;
|
gb->romVf = 0;
|
||||||
|
|
||||||
gb->pristineRom = 0;
|
gb->pristineRom = 0;
|
||||||
|
@ -126,6 +128,7 @@ void GBReset(struct LR35902Core* cpu) {
|
||||||
}
|
}
|
||||||
GBMemoryReset(gb);
|
GBMemoryReset(gb);
|
||||||
GBVideoReset(&gb->video);
|
GBVideoReset(&gb->video);
|
||||||
|
GBTimerReset(&gb->timer);
|
||||||
GBIOReset(gb);
|
GBIOReset(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +179,11 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testEvent = GBTimerProcessEvents(&gb->timer, cycles);
|
||||||
|
if (testEvent < nextEvent) {
|
||||||
|
nextEvent = testEvent;
|
||||||
|
}
|
||||||
|
|
||||||
cpu->cycles -= cycles;
|
cpu->cycles -= cycles;
|
||||||
cpu->nextEvent = nextEvent;
|
cpu->nextEvent = nextEvent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "lr35902/lr35902.h"
|
#include "lr35902/lr35902.h"
|
||||||
|
|
||||||
#include "gb/memory.h"
|
#include "gb/memory.h"
|
||||||
|
#include "gb/timer.h"
|
||||||
#include "gb/video.h"
|
#include "gb/video.h"
|
||||||
|
|
||||||
extern const uint32_t DMG_LR35902_FREQUENCY;
|
extern const uint32_t DMG_LR35902_FREQUENCY;
|
||||||
|
@ -40,6 +41,7 @@ struct GB {
|
||||||
struct LR35902Core* cpu;
|
struct LR35902Core* cpu;
|
||||||
struct GBMemory memory;
|
struct GBMemory memory;
|
||||||
struct GBVideo video;
|
struct GBVideo video;
|
||||||
|
struct GBTimer timer;
|
||||||
|
|
||||||
int* keySource;
|
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) {
|
void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||||
switch (address) {
|
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:
|
case REG_IF:
|
||||||
gb->memory.io[REG_IF] = value;
|
gb->memory.io[REG_IF] = value;
|
||||||
GBUpdateIRQs(gb);
|
GBUpdateIRQs(gb);
|
||||||
|
@ -108,6 +120,11 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||||
break;
|
break;
|
||||||
case REG_IE:
|
case REG_IE:
|
||||||
return gb->memory.ie;
|
return gb->memory.ie;
|
||||||
|
case REG_DIV:
|
||||||
|
case REG_TIMA:
|
||||||
|
case REG_TMA:
|
||||||
|
case REG_TAC:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO: Log
|
// TODO: Log
|
||||||
if (address >= GB_SIZE_IO) {
|
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