all repos — mgba @ 5373e7004a3b505482a685588553ded0a0d1d3d8

mGBA Game Boy Advance Emulator

DS: Start on video
Vicki Pfau vi@endrift.com
Tue, 31 Jan 2017 23:33:44 -0800
commit

5373e7004a3b505482a685588553ded0a0d1d3d8

parent

896749ede946fd761d327829e16cf70a47c76c0b

M include/mgba/internal/ds/ds.hinclude/mgba/internal/ds/ds.h

@@ -69,6 +69,7 @@

struct CircleBuffer fifo; }; +struct mCoreCallbacks; struct DS { struct mCPUComponent d;

@@ -99,6 +100,7 @@ struct VFile* bios7Vf;

struct VFile* bios9Vf; struct mKeyCallback* keyCallback; + struct mCoreCallbacks* coreCallbacks; }; struct DSCartridge {

@@ -161,6 +163,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
M include/mgba/internal/ds/io.hinclude/mgba/internal/ds/io.h

@@ -13,6 +13,10 @@

#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 @@ DS_REG_IF_HI = 0x216,

}; 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,
M include/mgba/internal/ds/memory.hinclude/mgba/internal/ds/memory.h

@@ -54,6 +54,7 @@ DS9_SIZE_DTCM = 0x00004000,

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,
M include/mgba/internal/ds/video.hinclude/mgba/internal/ds/video.h

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

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;

@@ -49,6 +48,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
M src/ds/ds.csrc/ds/ds.c

@@ -5,6 +5,7 @@ * 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/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 @@ DS9InterruptHandlerInit(&ds->ds9.cpu->irqh);

DSMemoryInit(ds); DSDMAInit(ds); + DSVideoInit(&ds->video); ds->video.p = ds; ds->ds7.springIRQ = 0;

@@ -223,6 +225,7 @@ mTimingClear(&ds->ds9.timing);

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 @@ ARMRaiseIRQ(cpu);

} } } + +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); + } +}
M src/ds/io.csrc/ds/io.c

@@ -29,6 +29,11 @@ }

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:
A src/ds/video.c

@@ -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? +}