mirror of https://github.com/mgba-emu/mgba.git
DS: Start on video
This commit is contained in:
parent
896749ede9
commit
5373e7004a
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
17
src/ds/ds.c
17
src/ds/ds.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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?
|
||||
}
|
Loading…
Reference in New Issue