DS: Start on video

This commit is contained in:
Vicki Pfau 2017-01-31 23:33:44 -08:00
parent 896749ede9
commit 5373e7004a
7 changed files with 218 additions and 14 deletions

View File

@ -69,6 +69,7 @@ struct DSCommon {
struct CircleBuffer fifo;
};
struct mCoreCallbacks;
struct DS {
struct mCPUComponent d;
@ -99,6 +100,7 @@ struct DS {
struct VFile* bios9Vf;
struct mKeyCallback* keyCallback;
struct mCoreCallbacks* coreCallbacks;
};
struct DSCartridge {
@ -162,6 +164,9 @@ void DSWriteIME(struct ARMCore* cpu, uint16_t* io, uint16_t value);
void DSWriteIE(struct ARMCore* cpu, uint16_t* io, uint32_t value);
void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq);
void DSFrameStarted(struct DS* ds);
void DSFrameEnded(struct DS* ds);
CXX_GUARD_END
#endif

View File

@ -13,6 +13,10 @@ CXX_GUARD_START
#include <mgba/core/log.h>
enum DSIORegisters {
// Video
DS_REG_DISPSTAT = 0x004,
DS_REG_VCOUNT = 0x006,
// DMA
DS_REG_DMA0SAD_LO = 0x0B0,
DS_REG_DMA0SAD_HI = 0x0B2,
@ -74,10 +78,6 @@ enum DSIORegisters {
};
enum DS7IORegisters {
// Video
DS7_REG_DISPSTAT = 0x004,
DS7_REG_VCOUNT = 0x006,
// Keypad
DS7_REG_KEYINPUT = 0x130,
DS7_REG_KEYCNT = 0x132,
@ -121,8 +121,6 @@ enum DS9IORegisters {
// Video
DS9_REG_A_DISPCNT_LO = 0x000,
DS9_REG_A_DISPCNT_HI = 0x002,
DS9_REG_DISPSTAT = 0x004,
DS9_REG_VCOUNT = 0x006,
DS9_REG_A_BG0CNT = 0x008,
DS9_REG_A_BG1CNT = 0x00A,
DS9_REG_A_BG2CNT = 0x00C,

View File

@ -54,6 +54,7 @@ enum {
DS7_SIZE_BIOS = 0x00004000,
DS9_SIZE_BIOS = 0x00008000,
DS_SIZE_RAM = 0x00400000,
DS_SIZE_VRAM = 0x000A4000,
DS_SIZE_WORKING_RAM = 0x00008000,
DS7_SIZE_WORKING_RAM = 0x00010000,
DS9_SIZE_PALETTE_RAM = 0x00000800,

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2017 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
@ -11,12 +11,15 @@
CXX_GUARD_START
#include <mgba/core/log.h>
#include <mgba/core/timing.h>
mLOG_DECLARE_CATEGORY(DS_VIDEO);
enum {
DS_VIDEO_HORIZONTAL_PIXELS = 256,
DS_VIDEO_HBLANK_PIXELS = 99,
DS7_VIDEO_HBLANK_LENGTH = 1613,
DS9_VIDEO_HBLANK_LENGTH = 1606,
DS_VIDEO_HORIZONTAL_LENGTH = (DS_VIDEO_HORIZONTAL_PIXELS + DS_VIDEO_HBLANK_PIXELS) * 6,
DS_VIDEO_VERTICAL_PIXELS = 192,
@ -29,17 +32,13 @@ enum {
struct DS;
struct DSVideo {
struct DS* p;
struct mTimingEvent event7;
struct mTimingEvent event9;
// VCOUNT
int vcount;
int32_t nextHblank;
int32_t nextEvent;
int32_t eventDiff;
int32_t nextHblankIRQ;
int32_t nextVblankIRQ;
int32_t nextVcounterIRQ;
uint16_t* vram;
int32_t frameCounter;
int frameskip;
@ -50,6 +49,9 @@ void DSVideoInit(struct DSVideo* video);
void DSVideoReset(struct DSVideo* video);
void DSVideoDeinit(struct DSVideo* video);
struct DSCommon;
void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value);
CXX_GUARD_START
#endif

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/ds/ds.h>
#include <mgba/core/interface.h>
#include <mgba/internal/arm/decoder.h>
#include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/arm/isa-inlines.h>
@ -115,6 +116,7 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
DSMemoryInit(ds);
DSDMAInit(ds);
DSVideoInit(&ds->video);
ds->video.p = ds;
ds->ds7.springIRQ = 0;
@ -223,6 +225,7 @@ void DS9Reset(struct ARMCore* cpu) {
CircleBufferInit(&ds->ds9.fifo, 64);
DSDMAReset(&ds->ds9);
DS9IOInit(ds);
DSVideoReset(&ds->video);
ds->activeCpu = cpu;
mTimingSchedule(&ds->ds9.timing, &ds->slice, SLICE_CYCLES);
@ -624,3 +627,17 @@ void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq) {
}
}
}
void DSFrameStarted(struct DS* ds) {
struct mCoreCallbacks* callbacks = ds->coreCallbacks;
if (callbacks && callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
}
void DSFrameEnded(struct DS* ds) {
struct mCoreCallbacks* callbacks = ds->coreCallbacks;
if (callbacks && callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
}

View File

@ -29,6 +29,11 @@ static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
switch (address) {
// Video
case DS_REG_DISPSTAT:
DSVideoWriteDISPSTAT(dscore, value);
break;
// DMA Fill
case DS_REG_DMA0FILL_LO:
case DS_REG_DMA0FILL_HI:

176
src/ds/video.c Normal file
View File

@ -0,0 +1,176 @@
/* Copyright (c) 2013-2015 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 <mgba/internal/ds/video.h>
#include <mgba/core/sync.h>
#include <mgba/internal/ds/ds.h>
#include <mgba/internal/gba/video.h>
#include <mgba-util/memory.h>
mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video");
static void _startHblank7(struct mTiming*, void* context, uint32_t cyclesLate);
static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate);
static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate);
static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate);
void DSVideoInit(struct DSVideo* video) {
video->vram = NULL;
video->frameskip = 0;
video->event7.name = "DS7 Video";
video->event7.callback = NULL;
video->event7.context = video;
video->event7.priority = 8;
video->event9.name = "DS9 Video";
video->event9.callback = NULL;
video->event9.context = video;
video->event9.priority = 8;
}
void DSVideoReset(struct DSVideo* video) {
video->vcount = 0;
video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
video->event7.callback = _startHblank7;
video->event9.callback = _startHblank9;
mTimingSchedule(&video->p->ds7.timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH);
mTimingSchedule(&video->p->ds9.timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH);
video->frameCounter = 0;
video->frameskipCounter = 0;
if (video->vram) {
mappedMemoryFree(video->vram, DS_SIZE_VRAM);
}
video->vram = anonymousMemoryMap(DS_SIZE_VRAM);
}
void DSVideoDeinit(struct DSVideo* video) {
mappedMemoryFree(video->vram, DS_SIZE_VRAM);
}
void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
video->event7.callback = _startHblank7;
mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
++video->vcount;
if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
video->vcount = 0;
}
video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
}
} else {
dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
}
video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
switch (video->vcount) {
case DS_VIDEO_VERTICAL_PIXELS:
video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
}
break;
case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
break;
}
}
void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
video->event7.callback = _startHdraw7;
mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
// Begin Hblank
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
}
video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
}
void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
video->event9.callback = _startHblank9;
mTimingSchedule(timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
++video->vcount;
if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
video->vcount = 0;
}
video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
}
} else {
dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
}
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
// Note: state may be recorded during callbacks, so ensure it is consistent!
switch (video->vcount) {
case 0:
DSFrameStarted(video->p);
break;
case DS_VIDEO_VERTICAL_PIXELS:
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
}
DSFrameEnded(video->p);
--video->frameskipCounter;
if (video->frameskipCounter < 0) {
mCoreSyncPostFrame(video->p->sync);
video->frameskipCounter = video->frameskip;
}
++video->frameCounter;
break;
case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
break;
}
}
void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
video->event9.callback = _startHdraw9;
mTimingSchedule(timing, &video->event9, DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
// Begin Hblank
dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
}
video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
}
void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
// TODO: Does a VCounter IRQ trigger on write?
}